Back to skill

Security audit

skills-firewall

Security checks for vulnerabilities and agentic risk

Overview

This looks like an advisory skill scanner rather than malware, but its firewall claims are stronger than its actual safety controls.

Treat this as a review-required advisory scanner, not as an enforcement firewall. It is not showing hidden exfiltration or destructive behavior, but do not rely on its allow/block result as a security boundary until fail-open handling, package identity binding, symlink containment, and HTML escaping are 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/scan_skill.py:171
Finding
Scan failures and missing targets are treated as safe<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scan_skill.py:171-180, 186-209, 232-239`; `scripts/firewall_check.py:153-161, 214-219` **Vulnerability Type**: Fail-open security decision **Risk Level**: High ### Vulnerable Code ```python # scripts/scan_skill.py:171-180 if not skill_path.exists(): return ScanResult( skill_name=skill_name, skill_path=str(skill_path), threat_level=ThreatLevel.SAFE.value, threats_found=[], warnings=[f"Skill path does not exist: {skill_path}"], recommendations=[], is_safe=True ) ``` ```python # scripts/scan_skill.py:186-209 for root, dirs, files in os.walk(skill_path): for file in files: if file.endswith(('.py', '.sh', '.js', '.ts', '.ps1', '.bat', '.md')): file_path = os.path.join(root, file) threats = scan_file(file_path, MALICIOUS_PATTERNS) all_threats.extend(threats) critical_count = sum(1 for t in all_threats if t["level"] == "critical") high_count = sum(1 for t in all_threats if t["level"] == "high") medium_count = sum(1 for t in all_threats if t["level"] == "medium") if critical_count > 0: threat_level = ThreatLevel.CRITICAL is_safe = False recommendations.append("CRITICAL: Immediate review required. Critical security threats detected.") elif high_count > 0: threat_level = ThreatLevel.HIGH is_safe = False recommendations.append("HIGH: Review recommended. High-risk patterns detected.") elif medium_count > 0: threat_level = ThreatLevel.MEDIUM is_safe = True recommendations.append("MEDIUM: Consider reviewing medium-risk patterns.") elif all_threats: threat_level = ThreatLevel.LOW is_safe = True recommendations.append("LOW: Minor concerns detected. Review optional.") else: threat_level = ThreatLevel.SAFE is_safe = True recommendations.append("No security concerns detected.") ``` ```python # scripts/scan_skill.py:232-239 try: with open(fi ...[truncated 3005 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Return an explicit error or `UNKNOWN`/`SCAN_ERROR` state for nonexistent paths, unreadable files, malformed input, and interrupted scans. 2. Never set `is_safe=True` unless every in-scope file was inspected successfully. 3. Make firewall decisions fail closed: incomplete scans should result in `BLOCK` or `QUARANTINE`, not `ALLOW`. 4. Record every skipped file and the reason it was skipped in the scan result. 5. Expand file detection beyond a fixed extension list. Consider file signatures, shebangs, executable permission bits, and relevant configuration formats. 6. Reject unsupported executable content or require manual review. 7. Replace broad exception suppression with narrow exception handling and actionable diagnostics. 8. Add tests covering nonexistent paths, permission errors, malformed files, unsupported executable extensions, and partial scans. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/firewall_check.py:174
Finding
Basename-only allowlisting bypasses all content inspection<![CDATA[ ## Vulnerability Details **File Location**: `scripts/firewall_check.py:174-185, 254-263, 303-309` **Vulnerability Type**: Insecure allowlist identity binding **Risk Level**: High ### Vulnerable Code ```python # scripts/firewall_check.py:174-185 def check_skill(self, skill_path: str) -> FirewallDecision: skill_path = Path(skill_path) skill_name = skill_path.name if skill_name in self.config.allowed_skills: return FirewallDecision( skill_name=skill_name, action=ActionType.ALLOW.value, reason="Skill is in allowed list", matched_rules=[], confidence=1.0 ) ``` ```python # scripts/firewall_check.py:254-263 def add_allowed_skill(self, skill_name: str): self.config.allowed_skills.add(skill_name) if skill_name in self.config.blocked_skills: self.config.blocked_skills.remove(skill_name) def add_blocked_skill(self, skill_name: str): self.config.blocked_skills.add(skill_name) if skill_name in self.config.allowed_skills: self.config.allowed_skills.remove(skill_name) ``` ```python # scripts/firewall_check.py:303-309 self.config.allowed_skills = set(config_dict.get("allowed_skills", [])) self.config.blocked_skills = set(config_dict.get("blocked_skills", [])) self.config.quarantine_dir = config_dict.get("quarantine_dir", "./quarantine") rules = [] for r in config_dict.get("rules", []): rules.append(FirewallRule( ``` ### Technical Analysis Allowlist membership is determined only from `Path(skill_path).name`, which is the final path component. The path, content digest, publisher, signature, and package provenance are not used to establish identity. The allowlist check occurs before `_read_skill_content` and before any security rules are evaluated. Therefore, matching an allowlisted basename produces an unconditional `ALLOW`, regardless of the supplied Skill’s contents. Directory basenames are attacker-controlled in normal package and fil ...[truncated 1183 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use a directory basename as a security identity. 2. Bind an allowlist entry to a canonical package identifier and a cryptographic digest of the reviewed content. 3. Where available, verify a signed package manifest and trusted publisher identity. 4. Resolve and normalize the complete path before comparison, while recognizing that path identity alone is insufficient when content can change. 5. Scan allowlisted Skills before applying exceptions. Exceptions should suppress only documented rules rather than bypassing all inspection. 6. Invalidate allowlist approval whenever the Skill’s content digest changes. 7. Store the approval reason, reviewer, timestamp, expected digest, and package provenance for auditability. 8. Add tests proving that unrelated directories with identical basenames do not inherit trust. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_report.py:297
Finding
Unescaped Skill names permit stored HTML injection in generated reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_report.py:297-306` **Vulnerability Type**: Stored HTML and script injection **Risk Level**: Medium ### Vulnerable Code ```python <h2>Threat Categories</h2> <div> {''.join(f'<div class="category-bar"><span class="category-name">{cat}</span><span class="category-count">{count}</span></div>' for cat, count in sorted(report.summary['by_category'].items(), key=lambda x: -x[1]))} </div> <h2>Detailed Findings</h2> {''.join(f'<div class="detail-item {"safe" if d["is_safe"] else "warning" if d["threat_level"] in ["low", "medium"] else "danger"}"><strong>{d["skill_name"]}</strong> - {d["threat_level"].upper()} ({d["threats_count"]} threats)</div>' for d in report.details)} <h2>Recommendations</h2> <div class="recommendations"> <ol> {''.join(f'<li>{rec}</li>' for rec in report.recommendations)} </ol> </div> ``` ### Technical Analysis Dynamic report values are directly interpolated into an HTML document without contextual escaping. In particular, `d["skill_name"]` originates from the scanned directory’s basename, which can contain HTML metacharacters on supported filesystems. An attacker-controlled name can terminate the intended markup and insert arbitrary elements, including event-handler attributes that execute JavaScript when the report is viewed. The generated report does not include a restrictive Content Security Policy that would mitigate inline script execution. The category and recommendation fields are currently generated primarily from internal constants, but they should also be escaped to preserve the security boundary if future rules or imported configuration make them attacker-controlled. ### Attack Path 1. An attacker creates or distributes a Skill directory with an HTML payload in its basename, such as a name containing an image element with an error event handler. 2. An auditor scans the parent directory and requests HTML output. 3. `generate_report.py` cop ...[truncated 946 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every dynamic HTML value with `html.escape(value, quote=True)` before interpolation. 2. Prefer a maintained template engine with automatic HTML escaping enabled by default. 3. Treat Skill names, imported rule names, descriptions, categories, recommendations, and all scan-derived strings as untrusted. 4. Add a restrictive Content Security Policy, for example disallowing inline scripts and limiting outbound connections. 5. Sanitize or visibly encode control characters in filenames before presenting them. 6. Add regression tests using names containing `<`, `>`, `"`, `'`, `&`, closing tags, and event-handler payloads. 7. Keep the bundled and generated reports free of inline executable content so that a strict policy can be applied. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/scan_skill.py:186
Finding
Symlinked source files allow scanning outside the requested Skill directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/scan_skill.py:186-191, 229-255`; `scripts/firewall_check.py:147-161` **Vulnerability Type**: Path-boundary violation through symlink following **Risk Level**: Medium ### Vulnerable Code ```python # scripts/scan_skill.py:186-191 for root, dirs, files in os.walk(skill_path): for file in files: if file.endswith(('.py', '.sh', '.js', '.ts', '.ps1', '.bat', '.md')): file_path = os.path.join(root, file) threats = scan_file(file_path, MALICIOUS_PATTERNS) all_threats.extend(threats) ``` ```python # scripts/scan_skill.py:229-255 def scan_file(file_path: str, patterns: List[ThreatIndicator]) -> List[Dict]: threats = [] try: with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: content = f.read() lines = content.split('\n') for indicator in patterns: matches = re.finditer(indicator.pattern, content, re.IGNORECASE) for match in matches: line_num = content[:match.start()].count('\n') + 1 line_content = lines[line_num - 1].strip() if line_num <= len(lines) else "" threats.append({ "pattern": indicator.pattern, "description": indicator.description, "level": indicator.level.value, "category": indicator.category, "file": file_path, "line": line_num, "matched_text": match.group()[:100], "line_content": line_content[:200] }) except Exception as e: pass return threats ``` ```python # scripts/firewall_check.py:147-161 def _read_skill_content(self, skill_path: str) -> str: content_parts = [] skill_path = Path(skill_path) for root, dirs, files in os.walk(skill_path): for file in files: if file.endswith(('.py', '.sh', ...[truncated 2365 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject symbolic links in untrusted Skill packages by checking each entry with `os.lstat` or `Path.is_symlink()`. 2. Resolve the Skill root once and resolve each candidate file before opening it. 3. Require every resolved candidate to remain beneath the resolved root, using a robust containment check such as `Path.is_relative_to` on supported Python versions. 4. Open files in a manner resistant to symlink races, such as directory-relative file descriptors with `O_NOFOLLOW` where available. 5. Apply limits for file size, total bytes scanned, nesting depth, and file count. 6. Avoid returning source-line content by default. If evidence is necessary, redact suspected secrets and require an explicit privileged option. 7. Report rejected links as scan errors and quarantine the package rather than silently ignoring them. 8. Add tests for file symlinks, symlink races, broken links, and targets outside the canonical Skill root. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (36)

Tp4

High
Category
MCP Tool Poisoning
Confidence
83% confidence
Finding
This is the same core issue in slightly different wording: the skill claims automatic firewall behavior, but the visible implementation context shows heuristic scanning/reporting rather than actual prevention or installation/execution controls. In a security product, overstating protection is dangerous because operators may skip other controls based on inaccurate expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
This is the same core issue in slightly different wording: the skill claims automatic firewall behavior, but the visible implementation context shows heuristic scanning/reporting rather than actual prevention or installation/execution controls. In a security product, overstating protection is dangerous because operators may skip other controls based on inaccurate expectations.

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python scripts/firewall_check.py /path/to/skill
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Subprocess with Shell
```python
subprocess.call(cmd, shell=True)   # HIGH: Command injection
subprocess.run(cmd, shell=True)    # HIGH: Command injection
subprocess.Popen(cmd, shell=True)  # HIGH: Command injection
```
Confidence
80% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Subprocess with Shell
```python
subprocess.call(cmd, shell=True)   # HIGH: Command injection
subprocess.run(cmd, shell=True)    # HIGH: Command injection
subprocess.Popen(cmd, shell=True)  # HIGH: Command injection
```
Confidence
80% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```python
subprocess.call(cmd, shell=True)   # HIGH: Command injection
subprocess.run(cmd, shell=True)    # HIGH: Command injection
subprocess.Popen(cmd, shell=True)  # HIGH: Command injection
```

### OS System Calls
Confidence
80% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```python
subprocess.call(cmd, shell=True)   # HIGH: Command injection
subprocess.run(cmd, shell=True)    # HIGH: Command injection
subprocess.Popen(cmd, shell=True)  # HIGH: Command injection
```

### OS System Calls
Confidence
80% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- **Severity**: HIGH
- **Rationale**: Privilege escalation is a security risk.

#### RULE-018: Block chmod 777
- **Pattern**: `chmod\s+777`
- **Action**: BLOCK
- **Severity**: MEDIUM
Confidence
80% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
FirewallRule(
        name="block_privilege_escalation",
        description="Block privilege escalation attempts",
        patterns=["sudo ", "su ", "chmod 777", "chown "],
        action=ActionType.BLOCK,
        enabled=True
    ),
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
FirewallRule(
        name="block_privilege_escalation",
        description="Block privilege escalation attempts",
        patterns=["sudo ", "su ", "chmod 777", "chown "],
        action=ActionType.BLOCK,
        enabled=True
    ),
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
FirewallRule(
        name="block_privilege_escalation",
        description="Block privilege escalation attempts",
        patterns=["sudo ", "su ", "chmod 777", "chown "],
        action=ActionType.BLOCK,
        enabled=True
    ),
Confidence
90% 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).

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
enabled=enabled
        )
        self.config.rules.append(rule)
        return rule
    
    def export_config(self, output_path: str):
        config_dict = {
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
| Network Communication | HTTP requests, socket connections | MEDIUM |
| File Operations | File deletion, modification | MEDIUM |
| Deserialization | pickle.loads, unsafe yaml.load | HIGH |
| Privilege Escalation | sudo, chmod 777 | HIGH |
| Obfuscation | Base64 decoding, encoding | LOW |

## Configuration
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
| Network Communication | HTTP requests, socket connections | MEDIUM |
| File Operations | File deletion, modification | MEDIUM |
| Deserialization | pickle.loads, unsafe yaml.load | HIGH |
| Privilege Escalation | sudo, chmod 777 | HIGH |
| Obfuscation | Base64 decoding, encoding | LOW |

## Configuration
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
| Network Communication | HTTP requests, socket connections | MEDIUM |
| File Operations | File deletion, modification | MEDIUM |
| Deserialization | pickle.loads, unsafe yaml.load | HIGH |
| Privilege Escalation | sudo, chmod 777 | HIGH |
| Obfuscation | Base64 decoding, encoding | LOW |

## Configuration
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Sudo/Su Commands
```python
os.system('sudo ...')    # HIGH: Privilege escalation
subprocess.run(['sudo', ...])  # HIGH: Privilege escalation
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Permission Changes
```python
os.chmod(file, 0o777)    # MEDIUM: Insecure permissions
chmod 777 file           # MEDIUM: Insecure permissions
```

## Risk Level Classification
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
- **Pattern**: `eval(`
- **Action**: BLOCK
- **Severity**: HIGH
- **Rationale**: eval() can execute arbitrary code from strings, leading to code injection vulnerabilities.

#### RULE-002: Block exec() Usage
- **Pattern**: `exec(`
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/firewall_check.py:71

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/scan_skill.py:46