Back to skill

Security audit

Diary Force

Security checks for vulnerabilities and agentic risk

Overview

This diary skill has a coherent purpose, but it can automatically persist and push sensitive diary and unrelated vault files to Git without adequate scoping or confirmation.

Install only if you are comfortable with diary text being written into local files, sent to an external OpenCode-backed model for analysis, committed into Git history, and pushed to the configured remote. Before using it, change the hardcoded paths, remove or disable automatic git push, replace git add . with staging only the intended file, and require review before any commit or upload.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/diary_force.py:457
Finding
Overbroad Git Staging Can Upload Unrelated Private Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/diary_force.py:457-458`, duplicated at `scripts/diary_force.py:490-491` and `scripts/think.py:125-126` **Vulnerability Type**: Excessive repository access and unintended data disclosure **Risk Level**: High ### Vulnerable Code ```python # Git push os.chdir(MEMORY_PATH.parent) os.system('git add . && git commit -m "memory: sync {}" && git push'.format(date)) ``` The same behavior appears in the dated finalization path: ```python # Git push os.chdir(MEMORY_PATH.parent) os.system(f'git add . && git commit -m "memory: sync {date}" && git push') ``` It is also used by the analysis workflow: ```python def git_push(date: str): """Git push""" os.chdir(MEMORY_PATH.parent) os.system('git add . && git commit -m "memory: sync {}" && git push'.format(date)) ``` ### Technical Analysis `MEMORY_PATH` is configured as a subdirectory: ```python MEMORY_PATH = Path("D:/ObsidianVault/ChuQuan/memory") ``` Before committing, the code changes the working directory to `MEMORY_PATH.parent`, which is the broader `ChuQuan` vault, and executes `git add .`. This stages every changed, unignored file below that repository directory rather than only the generated memory entry. The subsequent `git commit` and `git push` operations can therefore send unrelated personal notes, configuration files, documents, or credentials to the repository's configured remote. The operation is automatic and does not display the staged diff or request confirmation. ### Attack Path 1. A sensitive or attacker-selected file is created or modified anywhere below `D:/ObsidianVault/ChuQuan`. 2. The file is not excluded by `.gitignore`. 3. The user completes a diary or runs the analysis workflow. 4. The skill changes into the vault root. 5. `git add .` stages the sensitive file together with the generated diary content. 6. `git commit` records all staged files. 7. `git push` uploads the commit using the user's existing Git credentials ...[truncated 838 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Stage only the file generated by the current operation and invoke Git without a command shell: ```python import subprocess repository = MEMORY_PATH.parent relative_memory_file = memory_file.relative_to(repository) subprocess.run( ["git", "-C", str(repository), "add", "--", str(relative_memory_file)], check=True, ) subprocess.run( ["git", "-C", str(repository), "commit", "-m", f"memory: sync {date}"], check=True, ) subprocess.run( ["git", "-C", str(repository), "push"], check=True, ) ``` Additional hardening should include: 1. Verify that the generated file resolves inside the expected memory directory. 2. Never use `git add .`, `git add -A`, or equivalent repository-wide staging. 3. Inspect `git diff --cached --name-only` and reject any unexpected path before committing. 4. Require explicit user confirmation before the first remote push and whenever unexpected staged changes exist. 5. Check whether unrelated changes were already staged before the skill started; do not include them in the skill's commit. 6. Maintain a restrictive `.gitignore`, while treating it only as defense in depth. 7. Report the actual return status of every Git operation instead of always claiming that the push completed. 8. If sensitive data was already pushed, rotate exposed credentials and purge the data from Git history and all remote copies. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/think.py:59
Finding
OpenCode Invocation Uses an Unnecessary Command Shell with User-Controlled Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/think.py:59-65` **Vulnerability Type**: Unsafe subprocess invocation and potential command injection **Risk Level**: Medium ### Vulnerable Code ```python result = subprocess.run( ["opencode", "run", "--model", "opencode/minimax-m2.5-free", prompt], input="", capture_output=True, text=True, timeout=180, shell=True ) ``` The `prompt` value contains user-controlled diary input: ```python def call_opencode(user_input: str) -> str: """调用 OpenCode 进行分析""" prompt = THINKING_PROMPT.format(user_input=user_input) ``` ### Technical Analysis The process is launched with `shell=True` even though the command is already represented as an argument list and does not require shell functionality. This introduces a shell interpreter between the application and the intended executable. The precise treatment of a sequence combined with `shell=True` differs between operating systems and Python runtime behavior. The project uses Windows-style paths, making cross-platform shell semantics particularly relevant. Shell metacharacters, quoting sequences, and command separators contained in user-controlled prompt text may be interpreted unexpectedly rather than passed literally to OpenCode. Even where a particular Python and operating-system combination does not interpret the prompt as a shell command, this configuration remains unsafe and unreliable. It can alter argument handling, make executable resolution dependent on shell behavior, and become directly exploitable after seemingly minor platform or wrapper changes. ### Attack Path 1. An attacker supplies diary text containing shell metacharacters, command separators, or crafted quoting. 2. The text is inserted without validation into `THINKING_PROMPT`. 3. The resulting prompt is included in the subprocess argument sequence. 4. `subprocess.run` delegates execution through the platform shell because `shell=True`. 5. On an environment wh ...[truncated 909 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Remove shell invocation and pass arguments directly to the executable: ```python result = subprocess.run( [ "opencode", "run", "--model", "opencode/minimax-m2.5-free", prompt, ], input="", capture_output=True, text=True, timeout=180, shell=False, check=False, ) ``` Further hardening should include: 1. Resolve and validate the expected OpenCode executable path before execution. 2. Use `shutil.which("opencode")` or an administrator-configured absolute path. 3. Use a restricted environment rather than automatically inheriting unnecessary secrets. 4. Limit prompt length to prevent resource exhaustion and command-line length failures. 5. Catch `subprocess.TimeoutExpired`, `FileNotFoundError`, and other expected exceptions separately. 6. Never convert the argument list into a manually quoted command string. 7. Treat OpenCode output as untrusted before inserting it into persistent diary or memory files. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The Chinese description likewise omits or understates sensitive behaviors such as Git commits, remote push, and cross-directory persistence while emphasizing reminder and reflection features. In a diary context, such omissions materially increase privacy risk because personal reflections may be stored and propagated beyond the user's immediate expectation.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The Chinese description likewise omits or understates sensitive behaviors such as Git commits, remote push, and cross-directory persistence while emphasizing reminder and reflection features. In a diary context, such omissions materially increase privacy risk because personal reflections may be stored and propagated beyond the user's immediate expectation.

Missing User Warnings

High
Confidence
97% confidence
Finding
The workflow states that diary content may be analyzed via an external tool and then archived with Git, but the user-facing description lacks a clear upfront warning that private diary text will leave the immediate interaction boundary and be persisted. For intimate personal content, this creates a substantial confidentiality risk and undermines informed consent.

Ssd 3

High
Confidence
98% confidence
Finding
The skill is explicitly designed to retain, duplicate, and 'internalize' user diary content into a separate memory store and then sync it remotely. In a diary context, this creates a significant privacy risk because highly personal data is being copied to multiple locations under soft, non-transparent wording.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The manifest presents the skill as a diary reminder and analysis tool, but the code also synchronizes diary contents to a Git remote. That hidden expansion of capability is dangerous because users may share intimate personal reflections without realizing they will be transmitted and stored outside the local diary path.

Missing User Warnings

High
Confidence
99% confidence
Finding
The skill pushes diary content to a Git remote without an explicit warning that personal data will leave the local machine. In the context of a diary assistant, covert or insufficiently disclosed network transmission of intimate reflections is a severe privacy and data-exfiltration risk.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
Executing shell-based Git operations is not justified by the stated function of a habit-forming diary assistant. In this context, the extra capability is more dangerous because the tool handles highly sensitive user data and can unexpectedly stage, commit, and transmit it beyond the local system.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
# Git push
    os.chdir(MEMORY_PATH.parent)
    os.system('git add . && git commit -m "memory: sync {}" && git push'.format(date))
    
    return f"""
✅ **日记已完成!**
Confidence
97% confidence
Finding
The skill executes a shell command via os.system() to add, commit, and push diary contents to Git. Even though the interpolated date is usually locally generated, shell execution is unnecessary and dangerous for a diary tool, and this path transmits highly sensitive personal data to a remote repository without strong safety controls.

Missing User Warnings

High
Confidence
99% confidence
Finding
The alternate finalization path repeats the same risky behavior: it saves diary contents and pushes them to a remote repository without advance user-facing disclosure. Because this path accepts a date argument and performs the same sync automatically, it broadens the chance of unintended export of sensitive personal data.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
# Git push
    os.chdir(MEMORY_PATH.parent)
    os.system(f'git add . && git commit -m "memory: sync {date}" && git push')

    msg = f"""
✅日记已完成!
Confidence
99% confidence
Finding
This os.system() call interpolates a user-supplied date into a shell command. Although datetime.strptime validates format, invoking a shell for Git operations remains an unsafe pattern, and the function also pushes private diary content to a remote repository with no explicit consent at the point of transmission.

Context-Inappropriate Capability

High
Confidence
92% confidence
Finding
The skill executes external commands both for model invocation and for git operations, capabilities that exceed what is strictly necessary for diary reminder/analysis and introduce execution and exfiltration risks. In this context, the danger is higher because the processed content is highly personal diary text and the side effects include persistent file modification and possible remote sync.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
prompt = THINKING_PROMPT.format(user_input=user_input)
    
    try:
        result = subprocess.run(
            ["opencode", "run", "--model", "opencode/minimax-m2.5-free", prompt],
            input="",
            capture_output=True,
Confidence
90% confidence
Finding
The tool call forwards raw user input into an external model invocation with broad instruction context and no validation, review, or containment, enabling prompt-driven misuse of the downstream tool and uncontrolled transmission of sensitive content. While the subprocess arguments are not directly shell-injected here, this is still parameter abuse because untrusted diary text is passed into a powerful external analysis channel without guardrails.

Missing User Warnings

High
Confidence
97% confidence
Finding
The script automatically stages, commits, and pushes memory repository content without warning the user that local personal notes may be synchronized to a remote repository. In a diary tool, this is particularly dangerous because it can expose highly sensitive personal reflections beyond the local machine with a single run.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
def git_push(date: str):
    """Git push"""
    os.chdir(MEMORY_PATH.parent)
    os.system('git add . && git commit -m "memory: sync {}" && git push'.format(date))


def run_analysis(user_input: str):
Confidence
96% confidence
Finding
The script invokes a shell via os.system() to run git add/commit/push, which is dangerous because shell execution expands the attack surface and can publish local diary/memory content to a remote repository without an explicit confirmation step. Even though the date string is locally generated and not directly attacker-controlled, the behavior still performs sensitive side effects and relies on shell parsing unnecessarily.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
The title, tagline, and all usage documentation are presented only in Chinese, with no indication that users can opt into another language or that the skill is intentionally restricted to a Chinese-only audience for compliance or regional reasons. This matches the stated policy concern for language or locale restrictions without user opt-in.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README promotes automatic nightly diary prompting and memory archival for highly personal journal content, but provides no privacy notice, data handling description, retention policy, or consent guidance. Because diaries may contain sensitive mental health, relationship, work, or credential-related information, unattended collection and archival materially increase the risk of unexpected disclosure or over-collection.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The documented cron configuration causes the skill to run unattended every night and process personal journal data, yet the README does not warn users that scheduled execution may trigger automatic handling of sensitive content without a contemporaneous confirmation step. In the context of diary software, silent scheduled execution increases the chance of privacy surprises, unintended transmission to analysis components, and collection beyond user expectations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill describes file reads, file writes, and shell/Git operations but does not declare any explicit tool scope or permission boundaries. This is dangerous because a runtime may grant broader capabilities than users expect, enabling local diary access, persistence, and command execution without transparent consent or least-privilege controls.

Ssd 3

Medium
Confidence
94% confidence
Finding
The skill explicitly instructs persistent internalization and archival of diary content and derived insights. In the context of personal journaling, this is dangerous because it increases retention of sensitive emotional, behavioral, and situational data beyond the immediate task, expanding exposure in case of local compromise, sync, or accidental sharing.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger conditions are broad enough to activate on common phrases about journaling, reflection, low energy, or emotional fluctuation. In practice this can cause unintended invocation of a skill that reads/writes private diary data and may launch external analysis or archival steps without the user deliberately choosing that workflow.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The memory write and Git archival behavior is described as a normal workflow step without a strong privacy or integrity warning. Because memory extraction can distill sensitive personal insights into long-lived files and version history, users may unknowingly create durable records that are difficult to delete and easy to overexpose.

Ssd 3

Medium
Confidence
95% confidence
Finding
The instructions direct extraction of key insights from diaries into memory files and committing them to version control, creating durable plain-language records of potentially sensitive personal information. Git history can preserve secrets and intimate content even after later edits, making downstream disclosure and cleanup materially harder.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The eval prompts describe actions that can trigger file checks, interactive journaling, external tool use, memory writes, and Git commits, but they do not define clear activation boundaries, authorization checks, or confirmation requirements. In a forceful habit-building skill, broad prompts like checking whether a diary exists or internalizing entries into memory can cause unintended execution on loosely related user input, leading to privacy-invasive or state-changing behavior.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The eval explicitly expects the system to write derived memory entries into the memory/ directory and perform a Git commit, both of which are persistent state changes, without any warning, consent, or review step. Because this skill handles intimate diary content, automatic persistence and versioning can expose sensitive personal information, create irreversible history, and surprise users who only asked for analysis or journaling assistance.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The file header and interaction design are written as if the skill operates in Chinese by default, and the CLI prompts also instruct the user exclusively in Chinese. The policy explicitly flags language or locale constraints when they are imposed without user choice or justification.

Static analysis

No suspicious patterns detected.