Back to skill

Security audit

Diet Logger

Security checks for vulnerabilities and agentic risk

Overview

This is a straightforward diet logger, but its file-writing script can be steered to overwrite files outside the intended Obsidian folder.

Review this before installing if the agent may receive untrusted meal-log requests. The script should validate dates, reject path separators, avoid following symlinks, and constrain writes to the intended Obsidian daily-notes directory.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/log_diet.py:89
Finding

Unvalidated Date Input Permits Path Traversal and Symlink-Following File Overwrites

Content
View full analysis

Vulnerability Details

File Location: scripts/log_diet.py, lines 89–113
Vulnerability Type: Unvalidated filesystem path construction and unsafe file overwrite
Risk Level: Medium

python
parser.add_argument("--date", default=datetime.now().strftime("%Y-%m-%d"),
                    help="日期 (YYYY-MM-DD)")
parser.add_argument("--meal", required=True,
                    choices=["早饭", "中饭", "晚饭", "加餐"],
                    help="餐段")
parser.add_argument("--items", required=True,
                    help="食物列表,逗号分隔")

args = parser.parse_args()

# 确保目录存在
os.makedirs(DAILY_DIR, exist_ok=True)

# 文件路径
filename = f"饮食记录-{args.date}.md"
filepath = os.path.join(DAILY_DIR, filename)

# 读取或创建文件
content = read_or_create_file(filepath, args.date)

# 解析食物
items = parse_items(args.items)

# 更新内容
new_content = update_meal(content, args.meal, items)

# 保存文件
with open(filepath, "w", encoding="utf-8") as f:
    f.write(new_content)

Technical Analysis

The documentation states that --date must use the YYYY-MM-DD format, but the program does not enforce that constraint. The untrusted value is inserted directly into a filename and passed to os.path.join().

os.path.join() does not prevent traversal components embedded after a path separator. A crafted date containing separators and .. components can therefore resolve outside DAILY_DIR when the required intermediate directory exists. The destination is subsequently opened in write mode, which truncates an existing target before writing the generated Markdown content.

The implementation also uses ordinary open() operations that follow symbolic links. If the predictable daily-record path is a symbolic link, both the read and write operations act on the link target. There is no canonical-path containment check, symlink rejection, exclusive creation, or atomic replacement.

Attack Path

A path-traversal exploitati ...[truncated 1597 chars]

Remediation
View remediation

Remediation Suggestions

  1. Validate and canonicalize the date before using it:

    python
    parsed_date = datetime.strptime(args.date, "%Y-%m-%d")
    date_str = parsed_date.strftime("%Y-%m-%d")
    

    Reject the request if parsing fails.

  2. Construct the filename only from the canonical value, ensuring that user input cannot introduce separators or traversal components.

  3. Resolve and verify the destination path:

    python
    base = os.path.realpath(DAILY_DIR)
    filepath = os.path.realpath(
        os.path.join(base, f"饮食记录-{date_str}.md")
    )
    if os.path.dirname(filepath) != base:
        raise ValueError("Destination is outside the daily-record directory")
    
  4. Reject symbolic-link destinations using os.path.islink() and, where supported, open files with protections such as os.O_NOFOLLOW.

  5. Write through a securely created temporary file in the same directory and atomically replace the destination after validation. This reduces partial-write and corruption risks.

  6. Apply restrictive file permissions and ensure that untrusted local users cannot create files, directories, or symbolic links inside DAILY_DIR.

Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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
Findings (3)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill clearly instructs use of a local Python script that writes diet logs into a specific Obsidian directory, yet the manifest declares no explicit tool scope or permissions. This creates an authorization gap: an agent may gain file read/write capability implicitly without transparent restriction, making unintended filesystem access harder to govern or audit.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest description and user-facing documentation are entirely in Chinese and specify meal values and file naming in Chinese, which effectively imposes a language/locale requirement. The file does not offer an opt-in choice for language or explain a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and CLI help text specify usage, parameters, and meal choices entirely in Chinese. This imposes a fixed language on users without any opt-in or alternative locale handling, which matches the language/locale policy violation category.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.