T09 · Insecure Skill Coding Practices
- Location
scripts/obsidian_cli_plugins/git_ops.py:181- Finding
Unrestricted Git Staging Can Push Sensitive and Unrelated Vault Files
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is mostly coherent Obsidian automation, but its default synced writes can commit and push unrelated vault changes to a Git remote, which warrants review before installation.
Review this skill before installing if your Obsidian vault is backed by Git. Before using default sync-backed commands, check for unrelated or sensitive pending changes because they may be committed and pushed to the configured remote. Use local-only/no-sync modes for sensitive notes, keep secrets out of the vault repository, and be aware that uploaded media may be cached briefly and copied into durable Obsidian records when you request attachment recording.
scripts/obsidian_cli_plugins/git_ops.py:181Unrestricted Git Staging Can Push Sensitive and Unrelated Vault Files
The declared purpose is Obsidian vault automation, but the skill also instructs syncing itself into an OpenClaw skills directory, implying filesystem deployment and replacement operations outside the vault. That description-behavior mismatch is dangerous because users or agents may grant trust appropriate for note management while the skill also performs self-management actions affecting the broader agent runtime.
The skill advertises broad operational capabilities including shell execution, environment-variable use, and file read/write behavior, but the manifest provides no explicit tool-scope or permission boundary. In an agent ecosystem, that omission weakens least-privilege controls and can allow the skill to be invoked with more authority than users or orchestrators expect.
The skill describes claiming media uploads, staging readable local media paths, and later consuming staged attachments across messages. That introduces session persistence of user-provided artifacts, which can expose sensitive files across turns or to the wrong workflow if batch-keying, TTL enforcement, or isolation is weak.
This skill is the required component for Obsidian functionality. It owns vault discovery, Git preflight, record creation, attachment copying, staged-attachment consumption, and sync.
The `obsidian-media-claim` OpenClaw plugin is optional. It does not replace this skill and cannot create Obsidian records by itself. Its main purpose is to claim media-only channel uploads before OpenClaw sends them to the LLM, stage readable media paths for this skill, and avoid unnecessary token spend. If the plugin is absent, text records and explicit local `--attach` records still work through this skill, but media-only-then-text channel uploads need manual/runtime staging and may otherwise involve the model.
## Trigger Rules
This markdown file contains all user-facing operational guidance in Chinese, including command usage explanations and safety instructions, but it does not indicate that the user can choose another language. That creates a natural-language locale policy issue because the skill effectively forces a specific language without opt-in or documented regional justification.
The examples describe a default workflow that performs Git preflight, writes user content into the vault, and then commits and pushes it, but they do not clearly disclose that the user's text may be transmitted to a remote repository. This can cause unintentional publication or retention of sensitive notes, especially when users interpret 'sync' as a local vault operation rather than a remote Git push.
The attachment/media examples explain that files may be recorded into Obsidian, but they do not clearly warn that local files can be copied into the vault and preserved as permanent records. Users may share screenshots, audio, or documents assuming temporary handling, when the workflow may instead duplicate sensitive material into synced notes and any downstream backups or remotes.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
- Query available Obsidian native/plugin commands and explain whether commands such as `editor:attach-file` can add media.
- Add daily, weekly, monthly, quarterly, or yearly Tasks todos with Git preflight, Journals/template creation, correct target section placement, commit, and push.
- Show compact newly added tasks with `tasks show`, and show full task reports with `today-tasks --source` or `week-tasks --source`.
- Write short records such as `记录 王老师 183...` inline under the journal `记录` section by default; create independent QuickAdd `fleeting` record files only when explicitly requested.
- Copy explicitly attached media files into a record-local assets folder as Markdown embeds when supported, or ordinary Markdown links for non-previewable files.
- Safely read/search vault content without following outside-vault symlinks or returning sensitive private notes.
- Sync this skill into OpenClaw and verify whether the target runtime can actually discover it.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
- Analyze natural-language record requests with the Agent's own LLM/model before calling the local script, but first call `analyze-record --text "<original record content>" --date <date>` or `--prompt-only` to get the shared prompt/schema/current-date contract. Treat the user's utterance as context, not all as content, and use only the unified fields in `record-body.md`: `kind`, `headline`, `occurred_on`, `time_hints`, `scenes`, `actors`, `insight`, `question`, `reflection`, `next_actions`, and `intent`. Example: `今天又下雨了 突然灵光乍现 蚂蚁从飞机上掉下来会摔死吗 记录一下` on `2026-07-01` maps to `time_hints:["今天"]`, `occurred_on:"2026-07-01"`, `scenes:["下雨天"]`, `kind:"灵感"`, and `headline/question:"蚂蚁从飞机上掉下来会摔死吗"`. Semantic analysis is metadata only and must not replace the `--text` body. The record body config maps `fields` to `## 时间`, `## 场景`, `## 人物`, `## 灵感`, and other middle sections; appendix keywords stay reserved for `来源(Source)` and `关联(Reference)`.
- If Agent-side LLM/model analysis is unavailable or invalid, do not block record creation and do not omit the analysis parameter silently. Pass the original `--text` and omit unsupported analysis fields; do not add local hard-coded semantic heuristics, and do not describe hand-built JSON as successful semantic analysis.
- Use frontmatter `type` values `text`, `image`, `audio`, `video`, or `mixed` to classify the record.
- If the current user request includes, uploads, records, or otherwise supplies a media/file attachment, pass it as `--attach` in the same record command and pass `--require-attachment`; do not create the note first and ask whether to add the already supplied file. If the channel cannot send text and media together but exposes a readable local path, use `attachment-stage --path <path> --type <image|audio|video|file>` for the media-only message, then later call `attachment-pending --ttl-hours 48` and create the record with the returned `--staged-attachment <selector>` plus `--type mixed --require-attachment`. If the runti
...[truncated 26 chars]
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
OpenClaw-compatible installs should expose this skill as:
~/.openclaw/skills/obsidian-cli-plugins/SKILL.md
On older OpenClaw or OpenClaw-adjacent runtimes, ~/.cc-switch/skills/obsidian-cli-plugins/SKILL.md may still be useful as a compatibility copy. Do not assume that path is discoverable. Check the runtime's reported managedSkillsDir or run its skill discovery command first.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
OpenClaw-compatible installs should expose this skill as:
~/.openclaw/skills/obsidian-cli-plugins/SKILL.md
On older OpenClaw or OpenClaw-adjacent runtimes, ~/.cc-switch/skills/obsidian-cli-plugins/SKILL.md may still be useful as a compatibility copy. Do not assume that path is discoverable. Check the runtime's reported managedSkillsDir or run its skill discovery command first.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
OpenClaw-compatible installs should expose this skill as:
~/.openclaw/skills/obsidian-cli-plugins/SKILL.md
On older OpenClaw or OpenClaw-adjacent runtimes, ~/.cc-switch/skills/obsidian-cli-plugins/SKILL.md may still be useful as a compatibility copy. Do not assume that path is discoverable. Check the runtime's reported managedSkillsDir or run its skill discovery command first.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
OpenClaw-compatible installs should expose this skill as:
~/.openclaw/skills/obsidian-cli-plugins/SKILL.md
On older OpenClaw or OpenClaw-adjacent runtimes, ~/.cc-switch/skills/obsidian-cli-plugins/SKILL.md may still be useful as a compatibility copy. Do not assume that path is discoverable. Check the runtime's reported managedSkillsDir or run its skill discovery command first.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
OpenClaw-compatible installs should expose this skill as:
~/.openclaw/skills/obsidian-cli-plugins/SKILL.md
On older OpenClaw or OpenClaw-adjacent runtimes, ~/.cc-switch/skills/obsidian-cli-plugins/SKILL.md may still be useful as a compatibility copy. Do not assume that path is discoverable. Check the runtime's reported managedSkillsDir or run its skill discovery command first.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
Override the OpenClaw skills directory when needed:
OPENCLAW_SKILLS_DIR=/path/to/openclaw/skills python3 ~/.codex/skills/obsidian-cli-plugins/scripts/sync_openclaw.py
python3 ~/.codex/skills/obsidian-cli-plugins/scripts/sync_openclaw.py --dest /path/to/openclaw/skills
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
## Skill and plugin dependency
`obsidian-cli-plugins` is required for the Obsidian side of this workflow. It provides the commands that create notes, copy attachments into record assets, consume staged media, run Git preflight, commit, and sync.
`obsidian-media-claim` is optional. Install it only when OpenClaw channel uploads should be claimed before model dispatch. Its main value is cost and behavior control: media-only uploads are acknowledged and staged without asking the LLM to inspect or reason about the media. The later text instruction can then use this skill's `attachment-pending` and `--staged-attachment` workflow.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
`obsidian-cli-plugins` is required for the Obsidian side of this workflow. It provides the commands that create notes, copy attachments into record assets, consume staged media, run Git preflight, commit, and sync.
`obsidian-media-claim` is optional. Install it only when OpenClaw channel uploads should be claimed before model dispatch. Its main value is cost and behavior control: media-only uploads are acknowledged and staged without asking the LLM to inspect or reason about the media. The later text instruction can then use this skill's `attachment-pending` and `--staged-attachment` workflow.
Without the plugin, this skill still supports text records, task/project records, same-turn records with explicit readable `--attach` paths, and manually staged media. What is not guaranteed without the plugin is the no-model media-only upload path: OpenClaw may send the upload turn to the model, or the runtime may need another channel-specific guard to stage it.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
python3 ~/.openclaw/skills/obsidian-cli-plugins/scripts/obs_record_sync.py --mode file --period day --date today --text "" --topic --analysis-json ''
If the OpenClaw request includes any uploaded, recorded, or voice-input media/file attachment, it must create the record and attach the file in the same command. Use the same workflow for images, videos, audio, and ordinary files; media type only affects staging metadata and Markdown rendering:
```bash
python3 ~/.openclaw/skills/obsidian-cli-plugins/scripts/obs_record_sync.py --mode file --period day --date today --text "<original record content>" --type mixed --topic <kind> --analysis-json '<normalized model json>' --attach "<runtime-provided first path>" --attach "<runtime-provided second path>" --require-attachment --allow-external-attachments
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
1. The Agent runtime must expose a readable local file path for every media message before calling this skill. A URL, opaque media id, base64 blob hidden inside the channel, or expired temporary object is not enough unless the Agent first materializes it as a readable local file.
2. The Agent runtime must provide a stable grouping key across the media messages and the later text message. Prefer conversation id plus sender id plus message group id. If no group id exists, use a short timestamp bucket only when the Agent can verify the staged list before writing. Do not use `default` for cross-message record workflows.
3. The Agent must know whether the later text refers to all pending media or only a count/subset. If the user says `三张图` and five files are staged, ask or fail with an ambiguity message before writing. Do not guess.
4. If any required path or grouping capability is unavailable, stop before writing and report `unsupported-channel-attachment-record` or `attachment-path-unavailable`. Do not create a text-only record as a fallback unless the user explicitly asks for text-only recording after the warning.
Detailed state machine:
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
python3 ~/.openclaw/skills/obsidian-cli-plugins/scripts/obsidian_workflows.py attachment-stage --path "" --type <image|audio|video|file> --label "" --batch-key ""
The returned `attachment_id` is private cache state. Do not write the vault, do not create a journal link, and do not tell the user the Obsidian record has been created.
3. Repeat staging for every subsequent media-only message under the same `--batch-key`. Preserve the channel order when possible, but never rely on order alone for correctness.
4. When the text message arrives, first list staged media for that key with a command result, not model memory:
The documented default workflow automatically commits, pulls, and pushes vault changes as part of adding a task, without requiring an explicit user confirmation that unrelated local modifications may also be included. In an Obsidian vault, this can unintentionally sync sensitive notes, partially edited files, or other pending changes to a remote repository, creating confidentiality and integrity risks beyond the requested task addition.
The markdown repeatedly frames invocation and output around Chinese trigger phrases and section names, beginning with Chinese-only examples like 今日新增待办 and 今天新增任务. This indicates a language constraint without stating that users may choose another language or that the skill is region-specific, which matches the locale-policy concern.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
This skill may:
- Read and write Markdown files inside the resolved vault when the requested workflow requires it.
- Read Obsidian config files only to resolve vault names, paths, and open-vault state.
- Read `.obsidian/community-plugins.json` and plugin manifests/settings only for command discovery and workflow support.
- Run configured local CLIs such as `obsidian`, `git`, and the bundled Python scripts.
The attachment-clear command performs deletion-like cleanup of staged attachments via clear_staged_attachments(...), but this handler has no confirmation prompt and no user-facing disclosure beyond the parser name itself. For a destructive operation on locally staged user files/metadata, this file does not provide an explicit warning in code comments, prints, or prompts at the point of execution.
The manifest centers this skill on configured-vault automation, safe vault read/search, and vault-local records/attachments. Here, HTTP(S) attachment inputs are treated as valid sources without being constrained to vault-local files, introducing a network-oriented capability that is not justified or declared by the skill's stated purpose.
The skill relies on fixed Chinese headings such as 新增任务, 待办, and 记录 to detect target sections, which can make behavior dependent on a specific language/locale without offering a user choice. The policy allows locale constraints when explicitly documented and justified, but no such opt-in or justification appears in this file.
Detected: suspicious.dynamic_code_execution