T09 · Insecure Skill Coding Practices
- Location
scripts/tracker.py:46- Finding
Automatic Plaintext Duplication of Financial Data into a Potentially Synchronized Directory
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This finance skill appears purpose-built rather than malicious, but it automatically duplicates sensitive financial records into the skill directory and has inconsistent storage messaging that users should review carefully.
Install only if you are comfortable with your transaction history, balances, merchants, notes, and config being stored both under ~/.cai-gun-gun/data and copied into the skill's data directory. Treat exported Excel files as sensitive, avoid importing untrusted spreadsheets, and confirm destructive actions carefully.
scripts/tracker.py:46Automatic Plaintext Duplication of Financial Data into a Potentially Synchronized Directory
scripts/tracker.py:719Spreadsheet Formula Injection in Exported Excel Workbooks
The skill documents capabilities that imply environment access and file read/write behavior, but it does not declare any explicit tool scope or permission boundaries. In a financial-records skill, this weakens reviewability and increases the chance the agent can access or modify local financial data in ways the user did not clearly authorize.
The trigger list includes broad everyday phrases such as '记账' and '查账单', which may cause the skill to activate unintentionally during normal conversation. Because the skill can read, write, import, export, and potentially clear financial records, accidental invocation can expose private data or lead to unintended state changes.
The skill promotes third-party bill import but does not clearly warn that imported spreadsheets may contain highly sensitive personal financial information, including merchants, balances, dates, and account metadata. Users may provide such files without understanding retention, local copying, sync behavior, or the privacy exposure created by parsing them.
The skill describes automatic synchronization of financial data to a sync directory without a clear, prominent warning that sensitive records may be duplicated outside the primary storage location. Duplicating account and transaction data increases exposure, especially if the skill directory is backed up, synced, shared, or less protected than the main data directory.
The module description, user-facing command descriptions, help text, and output messages are written in Chinese, which effectively forces a specific language for interaction. There is no opt-in, alternative locale, or explanation that this skill is intentionally region-specific.
The code automatically performs bidirectional copying of financial records between the local data directory and the skill directory for sync purposes. In an agent ecosystem where skill directories may be packaged, synced, or exposed differently than local private storage, this broadens the confidentiality and integrity boundary for sensitive financial data beyond what the manifest describes.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
src = PRIMARY_DATA_DIR / filename
dst = SYNC_DATA_DIR / filename
if src.exists():
shutil.copy2(src, dst)
return True
except Exception as e:
print(f"同步到skill目录失败: {e}", file=sys.stderr)
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
src = PRIMARY_DATA_DIR / filename
dst = SYNC_DATA_DIR / filename
if src.exists():
shutil.copy2(src, dst)
return True
except Exception as e:
print(f"同步到skill目录失败: {e}", file=sys.stderr)
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 恢复到主目录
with open(TRANSACTIONS_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill() # 确保同步
print(f"从同步目录恢复交易记录", file=sys.stderr)
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
# 恢复到主目录
with open(TRANSACTIONS_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill() # 确保同步
print(f"从同步目录恢复交易记录", file=sys.stderr)
After saving transactions, the skill silently copies the data into the skill directory, creating an additional replica of sensitive financial records without an explicit user-facing notice at the time of write. Because bookkeeping data can include dates, merchants, notes, and account information, undisclosed replication materially increases privacy exposure.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
try:
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
with open(ACCOUNTS_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill()
return data
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
try:
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
with open(ACCOUNTS_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill()
return data
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
try:
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
with open(CATEGORIES_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill()
return data
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
try:
with open(sync_file, 'r', encoding='utf-8') as f:
data = json.load(f)
with open(CATEGORIES_FILE, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
sync_to_skill()
return data
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
def save_config(config: Dict):
"""保存配置(双存储)"""
PRIMARY_DATA_DIR.mkdir(parents=True, exist_ok=True)
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
sync_to_skill()
The export feature writes a full financial ledger to an Excel file on disk, which can disclose sensitive personal finance information if the location is shared, synced, or not expected by the user. Export itself is a legitimate feature, but the code provides no warning, safe default destination explanation, or privacy notice around file creation.
The skill exposes a destructive clear operation that wipes all transactions and resets balances, but this capability is not disclosed in the manifest description. Hidden destructive functionality increases the risk of accidental or unauthorized data loss, especially in agent-driven contexts where users may not expect irreversible state changes.
The documentation says data is stored locally and also describes automatic syncing to a skill directory, while the success template later says there is no cloud service and data is only local. For a finance tool, inconsistent storage claims can mislead users about where sensitive records are copied, retained, or recoverable, undermining informed consent and safe handling of financial data.