Back to skill

Security audit

Obsidian Daily Log

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent purpose, but its updater script can be steered to modify Markdown files outside the intended daily-notes folder if unsafe arguments are passed.

Review before installing. This appears to be a personal Obsidian logging helper, not an exfiltration or persistence tool, but the updater should validate dates and constrain writes to the daily-notes directory before use, especially in shared-chat workflows where unexpected input may influence logging commands.

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/update_daily_log.py:157
Finding

Unvalidated Date Argument Enables Path Traversal and Arbitrary Markdown File Modification

Content
View full analysis

Vulnerability Details

File Location: scripts/update_daily_log.py, lines 157 and 170-178
Vulnerability Type: Path traversal caused by insufficient input validation
Risk Level: Medium

Vulnerable Code

python
def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Append timestamped entries to an Obsidian daily note.")
    parser.add_argument("--date", required=True, help="Date in YYYY-MM-DD")
    parser.add_argument("--time", action="append", required=True, help="Time for one entry (repeat for multiple entries)")
    parser.add_argument("--text", action="append", required=True, help="Activity text for one entry (repeat in same order as --time)")
    parser.add_argument("--location", action="append", help="Optional location for one entry (repeat in same order)")
    parser.add_argument("--tags", action="append", help="Optional tags for one entry (repeat in same order)")
    parser.add_argument("--mode", choices=["bullets", "table"], default="bullets")
    parser.add_argument("--daily-dir", default=str(DEFAULT_DAILY_DIR))
    parser.add_argument("--template", default=str(DEFAULT_TEMPLATE))
    return parser.parse_args()
python
def main() -> int:
    args = parse_args()
    entries = build_entries(args)
    note_path = Path(args.daily_dir) / f"{args.date}.md"
    template_path = Path(args.template)

    ensure_note(note_path, args.date, template_path)
    content = note_path.read_text(encoding="utf-8")
    updated = update_timeline(content, entries, args.mode)
    note_path.write_text(updated, encoding="utf-8")

    print(str(note_path))
    return 0

The validation bypass is enabled by scripts/update_daily_log.py, lines 92-95:

python
def ensure_note(note_path: Path, date_str: str, template_path: Path) -> None:
    if note_path.exists():
        return
    note_path.parent.mkdir(parents=True, exist_ok=True)
    note_path.write_text(load_template(date_str, template_path), encoding="utf-8")
`
...[truncated 2882 chars]
Remediation
View remediation

Remediation Suggestions

  1. Validate the date unconditionally before any filesystem operation:

    python
    parsed_date = datetime.strptime(args.date, "%Y-%m-%d")
    safe_date = parsed_date.strftime("%Y-%m-%d")
    
  2. Construct the filename only from the normalized value:

    python
    note_path = Path(args.daily_dir) / f"{safe_date}.md"
    
  3. Resolve the base directory and destination and enforce containment:

    python
    daily_dir = Path(args.daily_dir).resolve()
    note_path = (daily_dir / f"{safe_date}.md").resolve()
    
    if note_path.parent != daily_dir:
        raise ValueError("Daily note path must remain inside the daily-note directory")
    
  4. Reject values containing path separators, absolute paths, parent-directory components, or any input that does not exactly match the required date representation.

  5. Perform validation before checking whether the destination exists so an existing file cannot bypass validation.

  6. If callers do not need configurable paths, remove or restrict --daily-dir and --template. Otherwise, validate them against an explicit allowlisted vault root.

  7. Add regression tests covering:

    • Valid YYYY-MM-DD dates.
    • ../ and ..\ traversal attempts.
    • Absolute-path input.
    • Traversal to existing and nonexistent files.
    • Encoded or mixed-separator path variants.
    • Verification that the resolved destination is a direct child of the configured daily-note directory.
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 (1)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill clearly instructs the agent to read and write files in a local Obsidian vault and to run a local update script, but it declares no explicit tool scope or allowed-tools restriction. That mismatch creates an overprivilege and transparency problem: the runtime may permit broader file operations than intended, and reviewers or policy systems cannot reliably constrain the skill to the minimum necessary access.

Content

No source excerpt is available for this finding.