Back to skill

Security audit

memory-tuner

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed local memory-maintenance helper, but users should understand it can persistently track and reorganize agent memory when enabled.

Install only if you want local, persistent tracking of which memory entries are used. Before enabling automation or running --mark, confirm OPENCLAW_WORKSPACE points to the intended workspace, review the dry-run report, and approve any archive or cleanup actions explicitly.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/memory-tuner.py:79
Finding

Unvalidated tracking records can manipulate retention decisions or terminate analysis

Content
View full analysis
= 3 and re.match(r"\d{4}-\d{2}-\d{2}", parts[0]): rows.append((parts[0][:10], parts[1], parts[2].lower())) ``` ```python for date, key, src in rows: idx = match_key(key, norm_key_map, norm_items) if idx < 0: continue s = stats[idx] w = USE_WEIGHT if src == "use" else SEARCH_GET_WEIGHT s["hits"] += 1 s["score"] += w s["sources"].add(src) d = dt.date.fromisoformat(date) if (today - d).days <= 30: s["hits_30d"] += 1 if (today - d).days <= 90: s["hits_90d"] += 1 if s["last_hit"] is None or d > s["last_hit"]: s["last_hit"] = d ``` ### Technical Analysis Tracking records are accepted when the date field merely begins with a date-shaped string. The implementation does not verify that: - The date is a valid calendar date. - The complete field conforms to the expected format. - The date is not in the future. - The source is one of `search`, `get`, or `use`. - The key is unambiguous and corresponds to an intended memory entry. A value such as `2026-99-99` passes the regular-expression check but raises `ValueError` in `datetime.date.fromisoformat`, terminating the report. A valid future date produces a negative age, which satisfies both `<= 30` and `<= 90`; this makes the associated entry appear recently accessed. Unknown source values are also accepted and counted with `SEARCH_GET_WEIGHT`, because every source other than `use` falls into the default branch. These statistics dete ...[truncated 1889 chars]
Remediation
View remediation
today: continue ``` 4. Enforce an allowlist for event sources: ```python if source not in {"search", "get", "use"}: continue ``` 5. Report malformed and rejected records with file names and line numbers without terminating the entire analysis. 6. Treat tracking records as advisory. Require exact entry verification and explicit confirmation before archival or cleanup. 7. Add tests for invalid dates, future dates, trailing date data, unknown sources, empty keys, and malformed separators. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/memory-tuner.py:89
Finding

Ambiguous fuzzy key matching can attribute activity to the wrong memory entry

Content
View full analysis
int: nk = norm_key(key) if nk in norm_key_map: return norm_key_map[nk] for i, (ik, isec) in enumerate(norm_items): if nk and (nk in ik or ik in nk or nk in isec or (isec and isec in nk)): return i return -1 ``` ```python def analyze(items, rows, today): norm_items = [(norm_key(i["key"]), norm_key(i["section"])) for i in items] norm_key_map = {nk: idx for idx, (nk, _) in enumerate(norm_items) if nk} stats = [{"hits": 0, "hits_30d": 0, "hits_90d": 0, "last_hit": None, "sources": set(), "score": 0.0} for _ in items] for date, key, src in rows: idx = match_key(key, norm_key_map, norm_items) ``` ### Technical Analysis Normalized keys are stored in a dictionary using the normalized value as the sole identifier. If two entries normalize to the same value, the later entry silently overwrites the earlier entry. When no exact normalized match exists, the code performs bidirectional substring matching against both item keys and section names. It returns the first matching item without determining whether additional entries also match. As a result, short, overlapping, duplicated, or section-like tracking keys can be attributed to an arbitrary entry. Since classifications are based on these attributed events, one entry can incorrectly receive another entry's activity while the intended entry remains untracked or becomes cold or frozen. The issue is especially significant because cold and frozen classifications feed recommendations to archive, merge, or clean memory content. ### Attack Path 1. `MEMORY.md` contains entries with duplicate or overlapping normalized identifiers, such as keys that differ only in punctuat ...[truncated 1303 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/memory-tuner.py:224
Finding

Dated cold and frozen markers written by the script are not recognized on subsequent runs

Content
View full analysis
", line)) ``` ```python def mark_items(items, report): if not os.path.exists(MEMORY_MD): return with open(MEMORY_MD, encoding="utf-8") as f: lines = f.readlines() changed = False for r in report: if r["grade"] not in ("cold", "frozen"): continue idx = r["line"] - 1 if idx >= len(lines) or "\n" changed = True if changed: with open(MEMORY_MD, "w", encoding="utf-8") as f: f.writelines(lines) ``` ### Technical Analysis The parser recognizes only comments whose content consists of a single word, such as: ```text ``` The `--mark` implementation writes a different format containing a colon and date: ```text ``` Because the parser's regular expression expects optional whitespace immediately after the word and then the comment terminator, it does not recognize the dated form generated by the script itself. Consequently, state written during one execution is not restored during the next execution. This breaks marker round-trip consistency and contradicts the documented behavior that dated cold and frozen annotations are respected. The implementation also skips any line containing an unrelated HTML comment, which can prevent a legitimate cold or frozen marker from being written. ### Attack Path 1. An entry is classified as cold or frozen. 2. The script is executed with `--mark`. 3. The entry is rewritten with a dated marker such as ``. 4. The script runs again. 5. `parse_memory` fails to re ...[truncated 1042 chars]
Remediation
View remediation
", line )) ``` 3. Parse and validate marker dates when they are used as lifecycle state. 4. Replace the broad `if "
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (9)

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 21)May include surrounding context.

md
- 热条目 → 上移到 MEMORY.md 更靠前的位置(引导注意)
- 冷条目 → 降级到 memory/archive/(内容保留,退出长远注入)
- 冻结条目 → 清理(先备份到 ~/.Trash)
- 常青条目(含 `<!-- keep -->`)→ 永不降级
- **超水位条目**(Redis allkeys 式)→ 按 lru/lfu 策略淘汰候选,降到水位以下

## 触发时机

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 21)May include surrounding context.

md
- 热条目 → 上移到 MEMORY.md 更靠前的位置(引导注意)
- 冷条目 → 降级到 memory/archive/(内容保留,退出长远注入)
- 冻结条目 → 清理(先备份到 ~/.Trash)
- 常青条目(含 `<!-- keep -->`)→ 永不降级
- **超水位条目**(Redis allkeys 式)→ 按 lru/lfu 策略淘汰候选,降到水位以下

## 触发时机

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 54)May include surrounding context.

python3 scripts/memory-tuner.py # 默认:分析 + 出报告(dry-run,lfu 策略) python3 scripts/memory-tuner.py --policy lru # 淘汰候选按 lru(最久未读)排序 python3 scripts/memory-tuner.py --policy lfu # 淘汰候选按 lfu(引用最少)排序(默认) python3 scripts/memory-tuner.py --mark # 额外在冷/冻条目上加 / 注释 python3 scripts/memory-tuner.py --json # 机器可读输出

text

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 54)May include surrounding context.

python3 scripts/memory-tuner.py # 默认:分析 + 出报告(dry-run,lfu 策略) python3 scripts/memory-tuner.py --policy lru # 淘汰候选按 lru(最久未读)排序 python3 scripts/memory-tuner.py --policy lfu # 淘汰候选按 lfu(引用最少)排序(默认) python3 scripts/memory-tuner.py --mark # 额外在冷/冻条目上加 / 注释 python3 scripts/memory-tuner.py --json # 机器可读输出

text

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill clearly describes capabilities that read environment state and modify workspace files, including tracking logs, archive files, and MEMORY.md maintenance, but it does not declare an explicit tool/permission scope. That mismatch increases the chance the skill is granted broader-than-expected file or environment access and makes risk review harder for operators.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manual trigger phrase includes broad natural-language requests like '优化记忆', which can easily overlap with ordinary user conversation. This can cause the skill to activate and propose or perform archival/deletion workflows during benign discussion, raising the risk of unintended memory modification.

Content

No source excerpt is available for this finding.

Tainted flow: 'MEMORY_MD' from os.environ.get (line 34, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/memory-tuner.py (reported line 236)May include surrounding context.

python
lines[idx] = lines[idx].rstrip("\n") + f" <!-- {r['grade']}: {dt.date.today()} -->\n"
        changed = True
    if changed:
        with open(MEMORY_MD, "w", encoding="utf-8") as f:
            f.writelines(lines)

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The optional automation guidance hard-codes the timezone as "Asia/Shanghai" in natural-language instructions. This imposes a locale-specific setting without indicating user choice, opt-in, or a region-specific compliance reason.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The top-level docstring presents the skill as '只读分析,默认不改文件', emphasizing read-only behavior. However, the documented --mark mode appends annotations to MEMORY.md via mark_items(), so the file is not purely read-only; the documentation softens this with '默认', but still frames the tool primarily as non-modifying while explicitly including a write mode.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.