Back to skill

Security audit

Canary

Security checks for vulnerabilities and agentic risk

Overview

This security-monitoring skill is not clearly malicious, but its protection claims are stronger than the implementation and it can store sensitive command or path data in plaintext logs.

Install only if you treat Canary as a best-effort advisory monitor, not a sandbox or reliable security boundary. Use OS permissions, containers, and real auditing for enforcement; avoid passing secrets in command strings; restrict log and tripwire file permissions; and verify any claimed rate-limit or protected-path behavior in your own environment before relying on it.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
canary.py:97
Finding
Protected Path Controls Can Be Bypassed Through Symbolic Links<![CDATA[ ## Vulnerability Details **File Location**: `canary.py`, lines 97-110 **Vulnerability Type**: Improper path canonicalization and unsafe prefix comparison **Risk Level**: High ### Vulnerable Code ```python # Expand home directory expanded_path = os.path.expanduser(path) abs_path = os.path.abspath(expanded_path) # Check against protected paths for protected in self.protected_paths: protected_expanded = os.path.expanduser(protected) protected_abs = os.path.abspath(protected_expanded) if abs_path.startswith(protected_abs): reason = f"Canary: Protected path access blocked: {path}" self._log_violation( 'critical', f"Attempted {operation} on protected path: {path}" ) return False, reason ``` ### Technical Analysis `os.path.abspath()` normalizes relative components such as `..`, but it does not resolve symbolic links. Consequently, the lexical path checked by Canary can differ from the filesystem object ultimately accessed. For example, if `/tmp/safe-link` is a symbolic link to `/etc`, checking `/tmp/safe-link/passwd` does not produce a path beginning with `/etc/`, even though opening that path accesses `/etc/passwd`. The use of `str.startswith()` is also not component-aware. If a protected path is configured as `/etc` without a trailing separator, an unrelated path such as `/etc-backup/file` is incorrectly classified as protected. This produces false positives in addition to the symlink-based false negatives. The project documentation acknowledges that symlink attacks may bypass path checks, but the weakness remains in the security enforcement implementation. ### Attack Path 1. An attacker or monitored agent creates a symbolic link in an unprotected directory: ```bash ln -s /etc /tmp/safe-link ``` 2. The agent asks Canary to validate the lexical path: ```python allowed, reason = canary.check_path( "/tmp/safe-link/passwd", "read" ) ``` 3. `os.path ...[truncated 961 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Canonicalize both candidate and protected paths using `Path.resolve()`: ```python candidate = Path(path).expanduser().resolve(strict=False) for protected in self.protected_paths: protected_path = Path(protected).expanduser().resolve(strict=False) if candidate == protected_path or protected_path in candidate.parents: return False, "Protected path access blocked" ``` 2. Use component-aware comparisons rather than raw string prefixes. 3. Revalidate the path immediately before the filesystem operation to reduce time-of-check/time-of-use exposure. 4. Where possible, open files relative to trusted directory descriptors and use platform controls that reject symbolic links, such as `O_NOFOLLOW`. 5. Add regression tests for: - Symlinks into protected directories. - Nested symlinks. - `..` traversal. - Paths that merely share a textual prefix. - Nonexistent write targets whose parent contains symlinks. 6. Continue to enforce OS-level permissions and sandboxing because application-level path checks cannot replace filesystem access controls. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
canary_tripwire.py:144
Finding
Tripwire Integrity Checks Can Generate Their Own Access Alerts<![CDATA[ ## Vulnerability Details **File Location**: `canary_tripwire.py`, lines 144-171 **Vulnerability Type**: Unreliable access-time monitoring and self-induced security alerts **Risk Level**: Medium ### Vulnerable Code ```python for path, info in self.tripwires.items(): tripwire_path = Path(path) # Check if file exists if not tripwire_path.exists(): alert = self._trigger_alert(path, 'DELETED', info['severity']) triggered.append(alert) continue # Check if modified try: with open(tripwire_path, 'r') as f: current_content = f.read() current_hash = self._hash_content(current_content) if current_hash != info['hash']: alert = self._trigger_alert(path, 'MODIFIED', info['severity']) triggered.append(alert) # Check access time (if supported) stat = tripwire_path.stat() created_time = datetime.fromisoformat(info['created']).timestamp() # If accessed after creation (with small buffer for system noise) if stat.st_atime > created_time + 60: alert = self._trigger_alert(path, 'ACCESSED', info['severity']) triggered.append(alert) ``` ### Technical Analysis The checker reads the tripwire file before inspecting its access timestamp. On filesystems that update access time for the read, Canary's own `open(..., 'r')` operation can change `st_atime`. Once the tripwire is more than 60 seconds old, that self-induced timestamp can satisfy the alert condition. The reverse failure is also possible. Filesystems mounted with `noatime`, or using delayed or relative access-time semantics, may not update `st_atime` when another process reads the file. A genuine unauthorized read may therefore remain undetected. The implementation also compares access time only to the original creation time. It does not maintain a reliable observation baseline that distinguishes Canary's own access from third-party access. Repea ...[truncated 1309 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not represent `st_atime` polling as dependable access detection. 2. Use OS-native monitoring facilities where read detection is required: - Linux Audit Framework or fanotify. - FSEvents or Endpoint Security on macOS. - Windows auditing or ETW-based monitoring. 3. If the heuristic is retained, collect metadata before Canary reads the file and avoid treating Canary's own subsequent access-time update as an external event. 4. Store and update a monitoring baseline rather than comparing every observation only with creation time. 5. Deduplicate repeated alerts for an unchanged event. 6. Clearly distinguish supported integrity events: - `DELETED`: polling-based existence check. - `MODIFIED`: content hash check. - `ACCESSED`: best-effort heuristic only. 7. Add tests under `strictatime`, `relatime`, and `noatime` behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
canary.py:97
Finding
Configured File and Network Rate Limits Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `canary.py`, lines 97-126 and 163-193; `config_example.json`, lines 37-41 **Vulnerability Type**: Missing enforcement of declared security controls **Risk Level**: Medium ### Vulnerable Code The example configuration declares three rate-limit categories: ```json "rate_limits": { "file_operations": {"limit": 100, "window": 60}, "network_requests": {"limit": 50, "window": 60}, "command_executions": {"limit": 20, "window": 60} } ``` The path-checking method does not invoke the rate limiter: ```python def check_path( self, path: str, operation: str = 'access' ) -> Tuple[bool, Optional[str]]: if self.halted: return False, "Canary: System halted due to safety violations" expanded_path = os.path.expanduser(path) abs_path = os.path.abspath(expanded_path) for protected in self.protected_paths: protected_expanded = os.path.expanduser(protected) protected_abs = os.path.abspath(protected_expanded) if abs_path.startswith(protected_abs): reason = f"Canary: Protected path access blocked: {path}" self._log_violation( 'critical', f"Attempted {operation} on protected path: {path}" ) return False, reason for pattern in self.forbidden_patterns: if re.search(pattern, path, re.IGNORECASE): reason = f"Canary: Forbidden pattern in path: {pattern}" self._log_violation( 'high', f"Forbidden pattern in path: {path}" ) return False, reason self._log_action(operation, path, 'info') return True, None ``` The generic rate-limit implementation is present, but only command checking calls it: ```python def _check_rate_limit(self, action_type: str) -> bool: limits = self.config.get('rate_limits', {}).get(action_type) if not limits: return True now = time.time() wind ...[truncated 2337 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce file-operation limits in `check_path()`: ```python if not self._check_rate_limit("file_operations"): reason = "Canary: File operation rate limit exceeded" self._log_violation("high", reason) return False, reason ``` 2. Add an explicit network-operation checking API if network limiting is part of the supported functionality. 3. Otherwise, remove the unused `network_requests` setting and avoid claiming that network requests are limited. 4. Validate rate-limit configuration types and reject zero, negative, missing, or excessively large values. 5. Define whether blocked attempts consume rate-limit capacity and apply that policy consistently. 6. Persist rate-limit and halt state if enforcement must survive process restarts. 7. Update documentation to state that auto-halt is an in-process deny flag and does not terminate or suspend an external agent. 8. Add automated tests proving that every advertised rate-limit category is actually enforced. 9. Correct `config_example.py`, which instructs users to pass a Python configuration even though `CanaryMonitor` accepts only `.json` files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
canary.py:140
Finding
Commands and File Paths Are Stored Verbatim in Plaintext Logs<![CDATA[ ## Vulnerability Details **File Location**: `canary.py`, lines 140-155 and 224-259 **Vulnerability Type**: Sensitive information exposure through insufficiently protected logging **Risk Level**: Medium ### Vulnerable Code Commands, including forbidden commands, are included verbatim in log messages: ```python for pattern in self.forbidden_patterns: if re.search(pattern, command, re.IGNORECASE): reason = f"Canary: Forbidden command pattern: {pattern}" self._log_violation( 'critical', f"Forbidden command blocked: {command}" ) return False, reason if not self._check_rate_limit('command_executions'): reason = "Canary: Command execution rate limit exceeded" self._log_violation( 'high', f"Rate limit exceeded for command: {command}" ) return False, reason self._log_action('command', command, 'info') ``` The complete target is written to a normal plaintext file without explicit restrictive permissions: ```python def _log_action(self, action_type: str, target: str, severity: str): """Log action to action log.""" entry = { 'timestamp': datetime.now().isoformat(), 'type': action_type, 'target': target, 'severity': severity, } self.action_log.append(entry) # Write to log file with open(self.log_file, 'a') as f: f.write(json.dumps(entry) + '\n') def _log_violation(self, severity: str, message: str): self.violation_count += 1 entry = { 'timestamp': datetime.now().isoformat(), 'severity': severity, 'message': message, 'violation_number': self.violation_count, } self.alert_history.append(entry) # Write to log file with open(self.log_file, 'a') as f: f.write(f"VIOLATION: {json.dumps(entry)}\n") ``` ### Technical Analysis Shell commands frequently contain confidential values, including: - API tokens passed as command-line arguments. - Aut ...[truncated 1755 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid logging complete command strings by default. 2. Record safer metadata such as: - Executable name. - Matched policy identifier. - A one-way hash of the complete command. - Redacted argument counts. 3. Redact common secret-bearing fields, including authorization headers, tokens, passwords, cookies, signed URL parameters, and connection strings. 4. Create log files with owner-only permissions, for example by using `os.open()` with mode `0o600`, and verify permissions on existing files. 5. Reject symbolic links and unsafe log destinations where practical. 6. Apply similarly restrictive permissions to: - `.canary_tripwires/registry.json` - `.canary_tripwires/alerts.log` - Exported JSON and Markdown reports. 7. Implement bounded log rotation and retention. 8. Provide a configuration option to disable successful-action logging or log metadata only. 9. Document that operators must not pass secrets directly on command lines and should prefer environment variables, standard input, or protected credential stores. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (69)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
Confidence
85% 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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
Confidence
85% 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
**Example:**
```python
# This is blocked:
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
Confidence
85% 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
canary.check_command('rm -rf /')

# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
canary.check_command('RM -RF /')    # Case may bypass patterns
```
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
# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
canary.check_command('RM -RF /')    # Case may bypass patterns
```

**Why:** Full semantic analysis requires complex parsing and execution simulation.
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
# This might NOT be blocked (obfuscated):
canary.check_command('r''m -rf /')  # Space in command name
canary.check_command('RM -RF /')    # Case may bypass patterns
```

**Why:** Full semantic analysis requires complex parsing and execution simulation.
Confidence
85% 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
**Limitation:** Canary checks actions but doesn't enforce sandboxing.

**Example:**
- Canary blocks `rm /etc/passwd`
- Agent can still try to run it (Canary just logs it)
- Enforcement depends on agent respecting Canary's response
Confidence
85% 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).

YARA rule 'agent_skill_destructive_autonomous_actions': Autonomous destructive filesystem, shell history, or repository actions in AI agent skills [agent_skills]

High
Category
YARA Match
Content
res OS-level isolation (containers, VMs, chroot).

**Workaround:**
- Run agent in container with limited permissions
- Use Canary + Docker/Podman for defense-in-depth
- Agent code must respect Canary checks (design agents to honor safety)

---

### 6. Pattern Bypass via Encoding

**Limitation:** Patterns can be bypassed with base64, hex, or other encoding.

**Example:**
```bash
# This is blocked:
rm -rf /

# This might NOT be blocked:
echo "cm0gLXJmIC8=" | base64 -d | sh  # Decodes to "rm -rf /"
```

**Why:** Detecting all encoding schemes is intractable.

**Workaround:**
- Add patterns for common encoding patterns (`base64 -d`, `echo | sh`)
- Monitor for unusual command sequences
- Review audit logs for suspicious activity

---

### 7. No Permission Enforcement

**Limitation:** Canary doesn't modify file system permissions.

**Example:**
- Canary protects `~/.ssh/`
- Agent could still access it if OS permissions allow
- Canary logs it, doesn't block at filesystem level

**Why:** Files
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Credential Access

High
Category
Privilege Escalation
Content
**Workaround:**
- Use OS permissions + Canary together
- Set restrictive file permissions (`chmod 600 ~/.ssh/id_rsa`)
- Run agent with limited user account

---
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

External Script Fetching

High
Category
Supply Chain
Content
**Forbidden Patterns:**
- Regular expression matching for dangerous commands
- Detects `rm -rf /`, `chmod 777`, `curl | sh`, etc.
- Extensible pattern library

**Rate Limiting:**
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
**Forbidden Patterns:**
- Regular expression matching for dangerous commands
- Detects `rm -rf /`, `chmod 777`, `curl | sh`, etc.
- Extensible pattern library

**Rate Limiting:**
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**Forbidden Patterns:**
- Regular expression matching for dangerous commands
- Detects `rm -rf /`, `chmod 777`, `curl | sh`, etc.
- Extensible pattern library

**Rate Limiting:**
Confidence
85% 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
**Forbidden Patterns:**
- Regular expression matching for dangerous commands
- Detects `rm -rf /`, `chmod 777`, `curl | sh`, etc.
- Extensible pattern library

**Rate Limiting:**
Confidence
85% 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
**Forbidden Patterns:**
- Regular expression matching for dangerous commands
- Detects `rm -rf /`, `chmod 777`, `curl | sh`, etc.
- Extensible pattern library

**Rate Limiting:**
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).

Static analysis

No suspicious patterns detected.