Back to skill

Security audit

Flomo to Obsidian Sync Tool

Security checks for vulnerabilities and agentic risk

Overview

This Flomo-to-Obsidian skill has a legitimate purpose, but it needs review because its password-based automation and local file handling are materially under-protected.

Prefer the safe browser-session mode and do not paste your Flomo password into chat. Avoid password mode until the skill removes false encryption claims, stops sourcing .env as shell code, uses safer credential storage, validates attachment paths, and provides clearer cron removal and containment guidance. Only convert exports you trust, especially if your Obsidian vault syncs to cloud storage.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/convert_v2.py:267
Finding
Untrusted attachment paths can copy arbitrary local files into the Obsidian vault<![CDATA[ ## Vulnerability Details **File Location**: `scripts/convert_v2.py:267-292` **Vulnerability Type**: Path traversal and unauthorized local file access **Risk Level**: High ### Vulnerable Code ```python def _process_attachments(self, notes: List[FlomoNote]) -> int: """处理并复制附件""" copied_count = 0 for note in notes: for attachment in note.attachments: src_path = self.flomo_html_dir / attachment['src'] if not src_path.exists(): logger.warning(f"附件不存在: {src_path}") continue # 生成新的文件名 filename = src_path.name dest_path = self.attachments_dir / filename # 如果文件已存在,添加时间戳避免冲突 if dest_path.exists(): stem = dest_path.stem suffix = dest_path.suffix timestamp = note.datetime.strftime('%Y%m%d%H%M%S') dest_path = self.attachments_dir / f"{stem}_{timestamp}{suffix}" try: shutil.copy2(src_path, dest_path) attachment['obsidian_path'] = f"attachments/{dest_path.name}" copied_count += 1 ``` The attachment source value is extracted directly from imported HTML: ```python src = img.get('src', '') if src: attachments.append({ 'type': 'image', 'src': src, 'alt': img.get('alt', 'image') }) ``` ### Technical Analysis The converter treats an attachment `src` attribute from an imported HTML document as a trusted filesystem path. Joining an attacker-controlled path with `self.flomo_html_dir` does not guarantee containment. A relative value containing traversal components, such as `../../sensitive-file`, can resolve outside the export directory. A platform-supported absolute path may also override or bypass the intended base directory. The code checks only whether the resulting path exists; it does not canonicalize the path, reject abs ...[truncated 1483 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject absolute attachment paths. 2. Canonicalize the export root and candidate source path before accessing the file. 3. Require the resolved source to remain inside the approved export attachment directory. 4. Require the source to be a regular file and reject symbolic links where appropriate. 5. Optionally allowlist expected attachment extensions and MIME types. 6. Log and skip invalid paths without including unnecessary sensitive path details. Example hardening: ```python export_root = self.flomo_html_dir.resolve() raw_src = Path(attachment["src"]) if raw_src.is_absolute(): logger.warning("Rejected absolute attachment path") continue src_path = (export_root / raw_src).resolve() try: src_path.relative_to(export_root) except ValueError: logger.warning("Rejected attachment path outside export directory") continue if not src_path.is_file() or src_path.is_symlink(): logger.warning("Rejected non-regular attachment") continue ``` If attachments are expected only under a `file` subdirectory, use that directory as the containment root rather than the entire HTML export directory. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
setup.sh:62
Finding
Flomo credentials are stored in plaintext while documentation claims encryption<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:62-85`; misleading security statement in `SKILL.md:380-384` **Vulnerability Type**: Plaintext credential storage and inaccurate security disclosure **Risk Level**: Medium ### Vulnerable Code ```bash if [ ! -f .env ]; then # 询问用户信息 read -p "请输入 Flomo 邮箱: " FLOMO_EMAIL read -sp "请输入 Flomo 密码: " FLOMO_PASSWORD echo "" read -p "请输入 Obsidian vault 路径 [~/Documents/Obsidian/flomo]: " OBSIDIAN_VAULT OBSIDIAN_VAULT=${OBSIDIAN_VAULT:-"~/Documents/Obsidian/flomo"} read -p "请输入标签前缀 [flomo/]: " TAG_PREFIX TAG_PREFIX=${TAG_PREFIX:-"flomo/"} # 创建 .env 文件 cat > .env << EOF # Flomo 账号配置 FLOMO_EMAIL=$FLOMO_EMAIL FLOMO_PASSWORD=$FLOMO_PASSWORD OBSIDIAN_VAULT=$OBSIDIAN_VAULT TAG_PREFIX=$TAG_PREFIX EOF echo "" echo "✅ 配置文件已创建: .env" # 设置文件权限(仅所有者可读写) chmod 600 .env ``` The Skill instructions state: ```text (这些信息会加密存储在本地,不会上传) ``` The actual configuration example also confirms direct plaintext storage: ```bash FLOMO_EMAIL=your-email@example.com FLOMO_PASSWORD=your-password OBSIDIAN_VAULT=/path/to/obsidian/vault/flomo TAG_PREFIX=flomo/ ``` ### Technical Analysis The password is written directly into `.env` without encryption. File mode `0600` is a useful access-control measure, but it is not encryption and does not protect the secret from: - Processes running under the same user. - Administrators or other privileged local actors. - Malware operating in the user's session. - Unencrypted backups or filesystem snapshots. - Accidental copying, archival, or diagnostic collection. The inaccurate encryption statement can cause users to select password mode based on a false understanding of how their credentials are protected. ### Attack Path 1. The user selects password mode and enters a Flomo password. 2. `setup.sh` writes the password directly into `.env`. 3. The Skill describes this storage as encrypted even though it is plaintext. 4. A same-us ...[truncated 677 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove every claim that `.env` credentials are encrypted. 2. Make browser-session mode the default and clearly explain that its profile contains sensitive authentication state. 3. For fully automated password mode, store credentials in an operating-system secret facility such as macOS Keychain, Windows Credential Manager, Linux Secret Service, or a properly configured server-side secret manager. 4. Retrieve the secret only at execution time and avoid persisting it in command history or logs. 5. If plaintext `.env` support remains, provide an explicit warning and require informed opt-in. 6. Continue applying mode `0600`, verify that the file is owned by the current user, and ensure it is excluded from source control and publication archives. 7. Recommend a unique Flomo password and account-level multifactor authentication where supported. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
sync.sh:9
Finding
The synchronization launcher executes the `.env` configuration file as shell code<![CDATA[ ## Vulnerability Details **File Location**: `sync.sh:9-32` **Vulnerability Type**: Arbitrary shell command execution through unsafe configuration loading **Risk Level**: High ### Vulnerable Code ```bash # 检查 .env 文件是否存在 if [ ! -f .env ]; then echo "❌ 错误:找不到 .env 文件" echo "" echo "请按以下步骤创建配置文件:" echo " 1. 复制模板: cp .env.example .env" echo " 2. 编辑文件: nano .env" echo " 3. 填入你的邮箱和密码" echo "" exit 1 fi # 加载环境变量 echo "📝 加载配置文件..." source .env # 检查必要的环境变量 if [ -z "$FLOMO_EMAIL" ] || [ -z "$FLOMO_PASSWORD" ]; then echo "❌ 错误:.env 文件中缺少必要配置" echo "" echo "请确保 .env 文件包含:" echo " FLOMO_EMAIL=你的邮箱" echo " FLOMO_PASSWORD=你的密码" echo "" exit 1 fi ``` ### Technical Analysis The shell built-in `source` interprets the entire `.env` file as Bash syntax. It does not limit the file to variable assignments. Command substitutions, function definitions, redirections, and arbitrary shell commands in the file execute with the privileges of the process running `sync.sh`. The risk is amplified by the optional cron configuration in `setup.sh:157-158`: ```bash (crontab -l 2>/dev/null || echo ""; echo "$CRON_LINE") | crontab - ``` Cron is a declared and user-confirmed automatic synchronization feature, so its presence is functionally justified. However, once configured, it causes a maliciously modified `.env` file to execute repeatedly and without an interactive user. ### Attack Path 1. An attacker gains the ability to modify, replace, or influence creation of the project's `.env` file. 2. The attacker adds shell syntax or command substitution instead of ordinary configuration data. 3. The user invokes `./sync.sh`, or an approved cron entry invokes it automatically. 4. Bash executes the injected content at `source .env`. 5. The injected commands run with the user's permissions and can access the user's files, credentials, network, and applications. 6. If cron is enabled, execution recurs according to the con ...[truncated 496 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use `source`, `.`, or `eval` to load configuration. 2. Parse only an explicit allowlist of keys: `FLOMO_EMAIL`, `FLOMO_PASSWORD`, `OBSIDIAN_VAULT`, and `TAG_PREFIX`. 3. Reject duplicate keys, malformed lines, command substitutions, shell metacharacters, and unexpected variables. 4. Prefer a structured format such as JSON parsed by Python. 5. Verify that the configuration is a regular file owned by the current user and not a symbolic link. 6. Retain restrictive file permissions. 7. When cron is configured, invoke a fixed executable with a fixed configuration path and document how to remove the task. A safer design is to move configuration parsing into Python: ```python import json from pathlib import Path config_path = Path("config.json") config = json.loads(config_path.read_text(encoding="utf-8")) allowed = {"email", "output", "tag_prefix"} if set(config) - allowed: raise ValueError("Unexpected configuration keys") ``` Credentials should be retrieved separately from an operating-system secret store rather than placed in an executable shell-format file. ]]>

T08 · Insecure Dependencies

Warning
Location
setup.sh:31
Finding
Setup installs mutable third-party dependencies without exact version or integrity pinning<![CDATA[ ## Vulnerability Details **File Location**: `setup.sh:31-38`; related dependency declarations in `scripts/requirements.txt:1-4` **Vulnerability Type**: Unpinned dependency installation and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code ```bash # 2. 安装 Python 依赖 echo "" echo "📥 安装 Python 依赖..." pip3 install playwright beautifulsoup4 markdownify -q echo "✅ Python 依赖安装完成" # 3. 安装 Playwright 浏览器 echo "" echo "📥 安装 Playwright 浏览器(可能需要几分钟)..." playwright install chromium ``` The requirements file uses only minimum-version constraints and does not include Playwright: ```text beautifulsoup4>=4.9.0 markdownify>=0.11.0 PyYAML>=6.0 lxml>=4.9.0 ``` ### Technical Analysis The setup script asks pip to install whichever package versions currently satisfy the unbounded dependency request. The requirements file similarly permits all future releases above a minimum version and does not provide cryptographic hashes. Python packages may execute code during installation or at import time. Consequently, the effective third-party code can change after the Skill has been reviewed. A compromised upstream release, maintainer account, dependency, package index response, or incompatible future version could introduce malicious or unsafe behavior. Playwright is installed directly by the setup script but is absent from `scripts/requirements.txt`, creating additional reproducibility and review inconsistencies. The browser installation also downloads a browser artifact through Playwright without a project-level lock manifest. ### Attack Path 1. The user runs `setup.sh`. 2. `pip3 install` queries the configured package index and resolves current package releases. 3. A compromised or malicious eligible package release is selected. 4. Package installation or subsequent import executes attacker-controlled code. 5. The code runs with the permissions of the user executing setup and can access that user's files and network. 6. Because versions are not locked, ...[truncated 593 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create and review a lock file containing exact dependency versions, including all transitive dependencies where supported. 2. Install with cryptographic hashes, for example through a hash-locked requirements file and `pip install --require-hashes`. 3. Add Playwright to the canonical dependency manifest and keep setup behavior consistent with that manifest. 4. Install dependencies inside a dedicated virtual environment rather than into the user's global Python environment. 5. Define upper bounds or exact versions for packages currently specified with `>=`. 6. Use only the official package index or an organization-controlled mirror with provenance and integrity controls. 7. Automate dependency vulnerability scanning and deliberate update review. 8. Pin and review the Playwright/browser revision used by the project where the tooling permits it. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (151)

Credential Access

High
Category
Privilege Escalation
Content
| 特性 | 🔐 安全模式 | 🤖 密码模式 |
|------|------------|-----------|
| **密码保存** | ❌ 不保存 | ✅ 保存在 .env |
| **首次使用** | 🔐 手动登录(5分钟) | 🤖 完全自动 |
| **后续同步** | 🤖 完全自动 | 🤖 完全自动 |
| **安全性** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
Confidence
91% confidence
Finding
The document explicitly states that password mode stores credentials in a `.env` file. While presented transparently and with some warnings, encouraging plaintext credential storage increases the chance of credential theft through local compromise, backups, accidental sharing, or misconfigured permissions.

Credential Access

High
Category
Privilege Escalation
Content
- ✅ **完全自动**:一次配置,永久有效
- ✅ **无人值守**:适合定时任务和服务器
- ✅ **不会过期**:登录凭证长期有效
- ✅ **设置简单**:只需配置 .env 文件

#### 缺点
- ⚠️ **密码保存**:需要在 .env 文件中保存密码
Confidence
93% confidence
Finding
This section promotes `.env`-based configuration for unattended operation, which normalizes persistent local password storage. In the context of an automation skill, that makes compromise more plausible because scheduled jobs and server deployments often widen access through logs, backups, shell history, or multi-user environments.

Credential Access

High
Category
Privilege Escalation
Content
- ✅ **设置简单**:只需配置 .env 文件

#### 缺点
- ⚠️ **密码保存**:需要在 .env 文件中保存密码
- ⚠️ **安全风险**:如果电脑被入侵,密码可能泄露
- ⚠️ **需要保护**:必须设置文件权限
Confidence
94% confidence
Finding
The text acknowledges the security risk but still instructs users to retain passwords in a local `.env` file. Even with file permissions, plaintext secrets remain recoverable by malware, local compromise, exposed backups, or accidental inclusion in archives and sync tools.

Credential Access

High
Category
Privilege Escalation
Content
#### 使用方法
```bash
# 1. 创建配置文件
cat > .env << EOF
FLOMO_EMAIL=your_phone_or_email
FLOMO_PASSWORD=your_password
EOF
Confidence
97% confidence
Finding
This snippet directly instructs users to create a `.env` file containing `FLOMO_EMAIL` and `FLOMO_PASSWORD` in plaintext. That is actionable credential-at-rest guidance, and if followed, exposes reusable account credentials to theft through local access, malware, backups, or accidental disclosure.

Credential Access

High
Category
Privilege Escalation
Content
EOF

# 2. 设置文件权限
chmod 600 .env

# 3. 运行同步
./sync.sh
Confidence
90% confidence
Finding
This line continues the plaintext credential workflow by instructing protection of the `.env` file rather than avoiding the risky storage pattern. Restrictive permissions help, but they do not eliminate exposure from endpoint compromise or operational leakage.

Credential Access

High
Category
Privilege Escalation
Content
### 从安全模式切换到密码模式
```bash
# 1. 创建密码配置
cat > .env << EOF
FLOMO_EMAIL=your_phone_or_email
FLOMO_PASSWORD=your_password
EOF
Confidence
97% confidence
Finding
This is another explicit example instructing users to create a plaintext `.env` file with account credentials. Because it provides exact steps to persist the username and password, it materially increases the likelihood of insecure secret handling by users.

Credential Access

High
Category
Privilege Escalation
Content
FLOMO_PASSWORD=your_password
EOF

chmod 600 .env

# 2. 使用密码模式
./sync.sh
Confidence
89% confidence
Finding
This line reinforces the insecure pattern by showing permission-hardening around a file that still contains plaintext credentials. The mitigation is partial and may create a false sense of safety because any compromise of the user context still exposes the secret.

Ssd 3

High
Confidence
99% confidence
Finding
This section instructs users to hand over their Flomo password directly to the AI, establishing a workflow for collecting sensitive secrets in plain-language conversation. In the context of an agent skill, that is especially dangerous because chat transcripts, tool logs, or third-party infrastructure may retain the credential beyond the immediate task.

Ssd 3

High
Confidence
98% confidence
Finding
The example dialogue reinforces the same insecure pattern by showing the AI asking for and receiving account credentials conversationally. Examples are powerful behavioral guidance; including this normalizes unsafe secret sharing and makes real-world credential exfiltration easier to disguise as legitimate setup.

Credential Access

High
Category
Privilege Escalation
Content
cd <skill-directory>

# 创建配置文件
cat > .env << EOF
FLOMO_EMAIL=your_phone_or_email
FLOMO_PASSWORD=your_password
EOF
Confidence
98% confidence
Finding
The documented password mode instructs users to place FLOMO_EMAIL and FLOMO_PASSWORD into a local .env file, which is plaintext secret storage. Even with restrictive file permissions and .gitignore, local malware, backups, misconfiguration, shell history, or accidental file disclosure can expose valid credentials; in this skill context, the agent is explicitly positioned to collect and use those credentials, making the risk more acute.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cd <skill-directory>

cp .env.example .env
```

### 步骤2:编辑配置
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## 🧪 验证设置

### 检查 .env 文件

```bash
cat .env
Confidence
98% confidence
Finding
The instruction to inspect `.env` with `cat` leads to full plaintext disclosure of credentials on the terminal. Because this skill requires real login credentials, the context makes plaintext display more sensitive than ordinary non-secret config files.

Credential Access

High
Category
Privilege Escalation
Content
### 检查 .env 文件

```bash
cat .env
```

应该看到:
Confidence
98% confidence
Finding
This is the concrete `cat .env` command, which reveals the stored flomo password and other sensitive values in plaintext. Exposure can persist in screenshots, support transcripts, shell capture tools, or shared terminals.

Credential Access

High
Category
Privilege Escalation
Content
```bash
# 重新加载环境变量
source .env

# 或重新运行脚本
./sync.sh
Confidence
96% confidence
Finding
Instructing users to run `source .env` is unsafe because `.env` is treated as shell code, not inert key-value data. If the file is modified maliciously or contains shell metacharacters/command substitutions, sourcing it can execute arbitrary commands in the user's shell.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill description frames the tool as parsing exported HTML and syncing notes, but the instructions also automate login to a remote service, handle user credentials, and perform browser-driven exports. That broader behavior materially changes the trust model because it introduces credential handling and remote account access that are not clearly declared in the top-level purpose/permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill description frames the tool as parsing exported HTML and syncing notes, but the instructions also automate login to a remote service, handle user credentials, and perform browser-driven exports. That broader behavior materially changes the trust model because it introduces credential handling and remote account access that are not clearly declared in the top-level purpose/permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill description frames the tool as parsing exported HTML and syncing notes, but the instructions also automate login to a remote service, handle user credentials, and perform browser-driven exports. That broader behavior materially changes the trust model because it introduces credential handling and remote account access that are not clearly declared in the top-level purpose/permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description frames the tool as parsing exported HTML and syncing notes, but the instructions also automate login to a remote service, handle user credentials, and perform browser-driven exports. That broader behavior materially changes the trust model because it introduces credential handling and remote account access that are not clearly declared in the top-level purpose/permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill description frames the tool as parsing exported HTML and syncing notes, but the instructions also automate login to a remote service, handle user credentials, and perform browser-driven exports. That broader behavior materially changes the trust model because it introduces credential handling and remote account access that are not clearly declared in the top-level purpose/permissions.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cd {skillDir}

cat > .env << EOF
FLOMO_EMAIL={email}
FLOMO_PASSWORD={password}
OBSIDIAN_VAULT={vault_path}
Confidence
98% confidence
Finding
The skill instructs collection of the user's flomo email and password and writes them into a local .env file. Plaintext credential storage creates a durable secret-at-rest that may be read by other local processes, exposed through backups, accidentally committed, or accessed if the machine is compromised.

Credential Access

High
Category
Privilege Escalation
Content
TAG_PREFIX={tag_prefix}
EOF

chmod 600 .env
```

##### 步骤3:测试同步
Confidence
97% confidence
Finding
This line is part of the same secret persistence flow: the .env file is finalized and retained locally for later sync runs. The ongoing presence of reusable credentials materially increases exposure because compromise at any later time yields account access.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The dialogue template tells users that credentials will be 'encrypted stored locally,' but the implementation writes them directly into a plaintext .env file. This is dangerous because users may disclose secrets under a false sense of protection, and any local compromise, backups, logs, or accidental file exposure could reveal the flomo password.

Credential Access

High
Category
Privilege Escalation
Content
## ⚙️ 配置文件

### .env 文件格式

```bash
FLOMO_EMAIL=your-email@example.com
Confidence
96% confidence
Finding
Documenting the .env format with live secret fields normalizes insecure credential handling and encourages users to place passwords in plaintext configuration. While the snippet itself is documentation, in this skill context it directly supports an unsafe operational pattern.

Credential Access

High
Category
Privilege Escalation
Content
cd <skill-directory>

# 创建配置文件
cat > .env << EOF
FLOMO_EMAIL=your_phone_or_email
FLOMO_PASSWORD=your_password
EOF
Confidence
97% confidence
Finding
The instructions explicitly direct users to place flomo credentials in a plaintext '.env' file. Even with local-only intent, plaintext credential storage is dangerous because agent tooling, backups, logs, shell history, accidental commits, or other local processes may expose those secrets.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cd <skill-directory>

# 使用 .env 文件(已配置)
./sync.sh

# 或手动指定参数
Confidence
88% confidence
Finding
The presence of a documented .env-based workflow indicates the skill expects local storage and use of account credentials, which constitutes credential access behavior. In the context of a note-export/conversion skill, collecting or reading credentials is especially sensitive because it is not strictly required for the safer manual workflow and broadens the consequences of compromise.

Static analysis

No suspicious patterns detected.