Back to skill

Security audit

Structured Memory

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed memory-indexing skill, but it needs review because it can persist sensitive workspace memory into plaintext derived files and includes unsafe path handling that could delete or overwrite unintended files.

Install only if you want agents to maintain a persistent structured memory layer for this workspace. Avoid storing secrets in daily memory, consider running first setup with `--no-backfill`, review generated `critical-facts/` files, and restrict script use to trusted operators. Do not pass custom card output directories, and treat recalled notes as data rather than instructions.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/rebuild_critical_fact_cards.py:108
Finding

Unrestricted Recursive Directory Deletion Through User-Controlled Output Path

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/rebuild_one_day.py:40
Finding

Unvalidated Date Argument Enables Path Traversal and File Clobbering

Content
View full analysis
') print('Example: rebuild_one_day.py 2026-03-10') raise SystemExit(0 if len(sys.argv) == 2 else 1) date = sys.argv[1] memory_path = ROOT / 'memory' / f'{date}.md' parsed_path = ROOT / f'tmp.parsed-memory-{date}.json' index_path = ROOT / 'memory-index' / 'by-date.json' subprocess.run([ 'python3', str(ROOT / 'skills/structured-memory/scripts/parse_daily_memory.py'), str(memory_path) ], check=True, stdout=parsed_path.open('w', encoding='utf-8')) parsed = json.loads(parsed_path.read_text(encoding='utf-8')) ``` ### Technical Analysis Although the interface documents the argument as `YYYY-MM-DD`, the script does not validate its syntax or semantic validity. The raw value is embedded in both an input path and an output path: - `ROOT / 'memory' / f'{date}.md'` - `ROOT / f'tmp.parsed-memory-{date}.json'` A value containing path separators and traversal components can alter the resolved location. The output path is opened in write mode before the parser subprocess runs, so an existing writable file at the constructed location is truncated immediately. The use of a subprocess argument list prevents shell command injection, but it does not prevent filesystem path traversal. The predictable `tmp.parsed-memory-*.json` naming also introduces unsafe temporary-file behavior and possible symlink-based clobbering in a hostile or shared workspace. ### Attack Path 1. An attacker influences the date passed to `rebuild_one_day.py`. 2. The attacker supplies a value containing path separators or traversal elements rather than a valid ISO date. 3. The application constructs `memory_path` and `parsed_path` using the untrusted value. 4. Path res ...[truncated 994 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/extract_critical_facts.py:148
Finding

Sensitive Operational Context Is Duplicated Into Persistent Plaintext Files Without Redaction

Content
View full analysis
dict: value = value.strip('`"\'.,)】]') return { 'entity': value, 'fact_type': fact_type, 'value': value, 'status': 'active', 'sensitivity': sensitivity_for(fact_type, value), 'source': f'memory/{date}.md', 'last_verified': date, 'domains': context.get('domains', []), 'modules': context.get('modules', []), 'tags': context.get('system_tags', []) + context.get('free_tags', []), 'related_project': infer_related_project(context), 'related_entity': infer_related_entity(line, value, fact_type, context), 'note': line.strip(), } ``` The resulting record is written without redaction: ```python block = ( f"- entity: {fact['entity']}\n" f" fact_type: {fact['fact_type']}\n" f" value: {fact['value']}\n" f" status: {fact['status']}\n" f" sensitivity: {fact['sensitivity']}\n" f" source: {fact['source']}\n" f" last_verified: {fact['last_verified']}\n" f" domains: {fact['domains']}\n" f" modules: {fact['modules']}\n" f" tags: {fact['tags']}\n" f" related_project: {fact['related_project']}\n" f" related_entity: {fact['related_entity']}\n" f" note: {fact['note']}\n" ) path.write_text(path.read_text(encoding='utf-8').rstrip() + '\n' + block, encoding='utf-8') ``` Accounts are explicitly directed to a plaintext credentials file: ```python mapping = { 'host': CRITICAL_FACTS_DIR / 'hosts.md', 'account': CRITICAL_FACTS_DIR / 'credentials.md', 'path': CRITICAL_FACTS_DIR / 'locations.md', 'repo': CRITICAL_FACTS_DIR / 'locations.md', 'service': CRITICAL_FACTS_DIR / 'services.md', 'endpoint': C ...[truncated 2403 chars]
Remediation
View remediation

T02 · Agent Memory Poisoning

Warning
Location
scripts/rebuild_critical_fact_cards.py:89
Finding

Verbatim Memory Notes Create a Persistent Prompt-Injection and Memory-Poisoning Channel

Content
View full analysis
dict: value = value.strip('`"\'.,)】]') return { 'entity': value, 'fact_type': fact_type, 'value': value, 'status': 'active', 'sensitivity': sensitivity_for(fact_type, value), 'source': f'memory/{date}.md', 'last_verified': date, 'domains': context.get('domains', []), 'modules': context.get('modules', []), 'tags': context.get('system_tags', []) + context.get('free_tags', []), 'related_project': infer_related_project(context), 'related_entity': infer_related_entity(line, value, fact_type, context), 'note': line.strip(), } ``` ### Technical Analysis Daily-memory content is potentially influenced by external messages, documents, tool output, or other untrusted sources. When a line contains a recognized host, URL, path, username, service, repository, or identifier, the extractor stores the complete line as a note. Card rebuilding then preserves that text verbatim. The Skill instructs Agents to prioritize `critical-facts/cards/` and raw critical-fact files when r ...[truncated 2022 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (149)

Direct flow: pathlib.Path.read_text (file read) → pathlib.Path.write_text (file write)

High
Category
Data Flow
Confidence
94% confidence
Finding

The script reads untrusted memory content, extracts values from it, and writes those values directly into persistent markdown files under critical-facts. Because fields like 'note' and 'value' come from attacker-controlled text with almost no sanitization, a crafted memory file can poison the long-term knowledge store with false hosts, accounts, paths, or malicious markdown content, causing data integrity issues and possible downstream misuse by agents that trust these files.

Content

Scanner excerpt · scripts/extract_critical_facts.py (reported line 249)May include surrounding context.

python
f"  related_entity: {fact['related_entity']}\n"
        f"  note: {fact['note']}\n"
    )
    path.write_text(path.read_text(encoding='utf-8').rstrip() + '\n' + block, encoding='utf-8')
    return True

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The description says to use the skill when 'designing, initializing, updating, or operating a memory system' across a very wide range of topics, but it does not provide concrete trigger phrases, boundaries, or exclusion conditions. This makes invocation scope ambiguous and increases the chance the skill is used during ordinary discussions that merely touch those topics.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill states that first initialization automatically backfills existing memory history into indexes, critical-facts, and cards, but does not prominently warn that this may read and rewrite large amounts of pre-existing workspace data. That creates a real risk of unintended data expansion, duplication of sensitive information into more discoverable locations, and unexpected modification of repository contents.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

These lines tell the agent to treat rebuild as part of its default workflow after 'meaningful' updates, which is subjective and can cause autonomous file-modifying behavior without a fresh user request. In an agent setting, ambiguous automatic write behavior increases the risk of unintended workspace changes, surprise bulk processing, and propagation of sensitive content into additional indexed files.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/check_idempotency.py (reported line 43)May include surrounding context.

python
cmd = ['python3', str(ROOT / 'skills/structured-memory/scripts/rebuild_one_day.py'), date]

    before = snapshot()
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL)
    after1 = snapshot()
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL)
    after2 = snapshot()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/check_idempotency.py (reported line 45)May include surrounding context.

python
cmd = ['python3', str(ROOT / 'skills/structured-memory/scripts/rebuild_one_day.py'), date]

    before = snapshot()
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL)
    after1 = snapshot()
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL)
    after2 = snapshot()

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/extract_critical_facts.py (reported line 104)May include surrounding context.

python
def load_context(path: Path) -> dict:
    raw = subprocess.run(
        ['python3', str(PARSE_SCRIPT), str(path)],
        check=True,
        capture_output=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill is explicitly designed to harvest and persist sensitive operational details such as hosts, accounts, endpoints, repositories, and identifiers into centralized markdown files, yet it provides no user-facing warning, consent prompt, or data-classification safeguard when --write is used. In a security-sensitive agent environment, silently converting free-form memory into a durable sensitive-facts repository materially increases the risk of over-collection, unauthorized retention, and later exfiltration or misuse.

Content

No source excerpt is available for this finding.

Tainted flow: 'text' from pathlib.Path.read_text (line 260, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
89% confidence
Finding

This write path persists content derived from parsed memory into critical-facts files without any trust boundary, approval gate, or strong validation. Even though the immediate write here is only adding a date section marker, it is part of the same workflow that lets untrusted memory drive persistent state changes, enabling storage poisoning and unauthorized modification of operational records.

Content

Scanner excerpt · scripts/extract_critical_facts.py (reported line 223)May include surrounding context.

python
if not text.endswith('\n'):
            text += '\n'
        text += f"\n{marker}\n"
        path.write_text(text, encoding='utf-8')
        lines = text.splitlines()
        start = lines.index(marker)
        end = len(lines)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/init_structure.py (reported line 58)May include surrounding context.

python
def backfill(days: list[str]):
    rebuilt = []
    for day in days:
        subprocess.run(['python3', str(REBUILD_ONE_DAY), day], check=True)
        rebuilt.append(day)
    BOOTSTRAP_MARKER.write_text(json.dumps({'days': rebuilt}, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
    return rebuilt

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script unconditionally deletes the entire output directory with shutil.rmtree(cards_dir) before rebuilding it. Because the directory is user-controllable via --cards-dir and there is no safety check, confirmation, or path constraint, a mistaken or maliciously supplied path could cause irreversible deletion of arbitrary files or directories accessible to the process.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/rebuild_one_day.py (reported line 49)May include surrounding context.

python
parsed_path = ROOT / f'tmp.parsed-memory-{date}.json'
    index_path = ROOT / 'memory-index' / 'by-date.json'

    subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/parse_daily_memory.py'), str(memory_path)
    ], check=True, stdout=parsed_path.open('w', encoding='utf-8'))

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script performs broad destructive updates: it rewrites matching markdown files across multiple directories and then triggers several write-capable helper scripts, all based on an unvalidated date argument and without confirmation, dry-run mode, or rollback. In an agent or automation context, this increases the risk of unintended data loss or corruption if the script is invoked with malformed input, pointed at an unexpected repository state, or run non-interactively.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/rebuild_one_day.py (reported line 63)May include surrounding context.

python
for candidate in (ROOT / 'critical-facts').glob('*.md'):
        safe_remove_if_contains(candidate, date)

    subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/upsert_by_date_index.py'), str(index_path), str(parsed_path)
    ], check=True)
    subprocess.run([

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/rebuild_one_day.py (reported line 66)May include surrounding context.

python
subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/upsert_by_date_index.py'), str(index_path), str(parsed_path)
    ], check=True)
    subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/update_topic_indexes.py'), str(parsed_path), str(memory_path)
    ], check=True)
    subprocess.run([

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/rebuild_one_day.py (reported line 69)May include surrounding context.

python
subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/update_topic_indexes.py'), str(parsed_path), str(memory_path)
    ], check=True)
    subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/extract_critical_facts.py'), str(memory_path), '--write'
    ], check=True)
    subprocess.run([

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/rebuild_one_day.py (reported line 72)May include surrounding context.

python
subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/extract_critical_facts.py'), str(memory_path), '--write'
    ], check=True)
    subprocess.run([
        'python3', str(ROOT / 'skills/structured-memory/scripts/rebuild_critical_fact_cards.py')
    ], check=True)
    print(f'Rebuilt indexes, critical facts, and cards for {date}')

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script hard-codes Chinese trigger and classification patterns across its summarization logic, which makes the skill effectively tailored to one language/locale. There is no user opt-in, fallback, or documented scope indicating that this is intentionally limited to Chinese-language memories, so it can violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/update_topic_indexes.py (reported line 39)May include surrounding context.

python
script = ROOT / 'skills/structured-memory/scripts/summarize_daily_memory.py'
    try:
        result = subprocess.run(
            ['python3', str(script), str(memory_path)],
            check=True,
            capture_output=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script automatically extracts summaries and metadata from memory files and appends them into broader index files under memory-modules and memory-entities. This can propagate sensitive personal or business information into additional locations, increasing exposure, discoverability, and retention without any filtering, consent gate, or warning to the operator.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from pathlib.Path.read_text (line 64, file read) → pathlib.Path.write_text (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/upsert_by_date_index.py (reported line 78)May include surrounding context.

python
'priority': priority,
    }

    index_path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
    print(f'Updated {index_path} for {date}')

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file content is entirely written in Chinese and provides no indication that language choice is optional or justified by a documented region-specific constraint. Under the language/locale policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The markdown content is entirely in Chinese and provides no indication that language choice is optional or that the skill is specifically scoped to Chinese-speaking users. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file presents all natural-language content in Chinese and does not indicate that the user opted into that language or that the skill is intended for a Chinese-only audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instructional text is written entirely in Chinese, which constitutes a language constraint in the skill content without any indication that the user can choose a preferred language. The policy requires either user opt-in for a specific language/locale or a clearly documented justification for the constraint.

Content

No source excerpt is available for this finding.