Back to skill

Security audit

engram

Security checks for vulnerabilities and agentic risk

Overview

Engram is a disclosed local memory tool, but it grants persistent workspace influence and shell/file access broad enough that users should review it carefully before installing.

Install only in a workspace where you are comfortable giving a memory tool persistent local state, optional cron jobs, and the ability to run user-specified shell evidence commands. Review the migration report before applying, avoid --yes rewire unless you have read the changes, never store evidence commands from untrusted text, and treat Engram logs, backups, and battery reports as sensitive workspace data.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (53)

Tainted flow: 'req' from os.environ.get (line 291, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · engine/engram-sleep.py (reported line 267)May include surrounding context.

python
return _LLM_STATS["available"]
    try:
        req = urllib.request.Request("http://localhost:11434/api/tags")
        with urllib.request.urlopen(req, timeout=5) as resp:
            data = json.loads(resp.read().decode("utf-8"))
        names = {m.get("name", "") for m in data.get("models", [])}
        _LLM_STATS["available"] = OLLAMA_MODEL in names

Tainted flow: 'req' from os.environ.get (line 291, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · engine/engram-sleep.py (reported line 296)May include surrounding context.

python
)
    t0 = time.time()
    try:
        with urllib.request.urlopen(req, timeout=OLLAMA_TIMEOUT) as resp:
            data = json.loads(resp.read().decode("utf-8"))
    except Exception as e:
        sys.stderr.write(f"[ollama] call failed: {e}\n")

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Confidence
75% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · CHANGELOG.md (reported line 18)May include surrounding context.

md
om pasted text). Neither is a
  memory-graph capability and SKILL.md never listed them. ClawHub's SkillSpector scan
  of 1.2.1 called them undeclared; it was right.

### Changed

- SKILL.md description now lists every shipped script and what it does, and a new
  "Powers, up front" section states the full blast radius in five lines: SQLite under
  the workspace, opt-in edits to MEMORY.md/AGENTS.md/crontab with a prompt, evidence
  strings run through the shell, localhost Ollama only. The scanner judged every
  helper file against a one-paragraph description and reported nine "mismatches";
  those files are now named in the description they are judged against.
- The task-ledger section says plainly that evidence commands are code, not data.



### Removed

- The `engram creds` command is gone. It searched nodes for token/password/key
  properties and then globbed every `.env` and `secrets/*.env` under `projects/` and
  printed what it found. A memory skill has no business being a secret

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| `engram.py` | The engine. store, recall, msgctx, task, link, tags, wip, startup. **Generated — see below.** |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
| `engram-battery.py` | Larger adversarial recall battery |

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
85% confidence
Finding

os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Content

Scanner excerpt · engine/bq.py (reported line 72)May include surrounding context.

python
def fallback(argv):
    os.execvp(sys.executable, [sys.executable, ENGRAM_PATH] + argv)


def main():

Memory Manipulation

High
Category
Memory Poisoning
Confidence
93% confidence
Finding

This function overwrites MEMORY.md with Engram-managed content, replacing potentially important user or agent context with tool-controlled instructions. Although the script creates a backup and prompts first, altering memory/state files is security-relevant because it can steer future agent behavior and reduce visibility into prior instructions or evidence.

Content

Scanner excerpt · engine/engram-rewire.py (reported line 92)May include surrounding context.

python
def slim_memory_md():
    """Replace MEMORY.md with a slim pointer to Engram."""
    mem_path = os.path.join(WORKSPACE, "MEMORY.md")
    content = """# MEMORY.md (Engram-managed)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
85% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · engine/engram-rewire.py (reported line 204)May include surrounding context.

python
def main():
    print("Engram Rewire")
    print("=" * 50)
    print("This will: back up MEMORY.md and AGENTS.md, replace MEMORY.md with a pointer,")
    print("add an Engram section to AGENTS.md, and append 3 marked lines to your crontab.")
    print("engram-uninstall.py reverts all of it.")
    if "--yes" not in sys.argv:

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

cmd_context reads a file path from node metadata (source_file) and opens it directly from the workspace without constraining it to an approved subtree or validating traversal. Because nodes can be created or modified through the same CLI, a malicious node can point to sensitive local files and cause unintended file disclosure when context is run.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The task subsystem extends a memory tool into an arbitrary shell execution surface by storing evidence commands and later running them automatically. In an agent setting, this is especially dangerous because seemingly benign memory or workflow operations can persist executable payloads that fire later during probing or task completion, creating a confused-deputy/RCE path.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is a concrete tool-parameter abuse issue: a field stored as task metadata (evidence) is later treated as executable shell input. In an agent skill context, that allows data-to-code escalation, where an attacker can smuggle commands into stored state and have the agent execute them during routine lifecycle actions.

Content

Scanner excerpt · engine/engram.py (reported line 5477)May include surrounding context.

python
return None
    import subprocess
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, timeout=timeout)
        return r.returncode == 0
    except Exception:
        return False

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documentation describes capabilities including shell execution, file read/write, environment access, and optional localhost network use, but it does not declare an explicit tool scope such as permissions or allowed-tools. That omission weakens review and policy enforcement because consumers cannot easily constrain what the skill is expected to use, especially given high-risk features like shell execution of task evidence and file/crontab modification.

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 · engine/engram-battery.py (reported line 117)May include surrounding context.

python
def run_msgctx(message):
    """Returns (stdout, stderr, returncode). Streams are kept apart on purpose."""
    try:
        r = subprocess.run(BQ + ['msgctx', message],
                           capture_output=True, text=True, timeout=60, cwd=WS)
        return (r.stdout or ''), (r.stderr or ''), r.returncode
    except subprocess.TimeoutExpired:

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 · engine/engram-battery.py (reported line 133)May include surrounding context.

python
broken = []
    n = hit = 0
    # cleanup any stale copy first, then arm fresh
    subprocess.run(BQ + ['delete', '--name', INTENTION_NODE_NAME], capture_output=True, text=True, cwd=WS)
    arm = subprocess.run(BQ + ['intend', INTENTION_TRIGGER, INTENTION_ACTION],
                         capture_output=True, text=True, cwd=WS)
    armed_ok = 'Armed' in (arm.stdout + arm.stderr) or 'Stored' in (arm.stdout + arm.stderr)

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 · engine/engram-battery.py (reported line 157)May include surrounding context.

python
broken = []
    n = hit = 0
    # cleanup any stale copy first, then arm fresh
    subprocess.run(BQ + ['delete', '--name', INTENTION_NODE_NAME], capture_output=True, text=True, cwd=WS)
    arm = subprocess.run(BQ + ['intend', INTENTION_TRIGGER, INTENTION_ACTION],
                         capture_output=True, text=True, cwd=WS)
    armed_ok = 'Armed' in (arm.stdout + arm.stderr) or 'Stored' in (arm.stdout + arm.stderr)

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 · engine/engram-battery.py (reported line 134)May include surrounding context.

python
n = hit = 0
    # cleanup any stale copy first, then arm fresh
    subprocess.run(BQ + ['delete', '--name', INTENTION_NODE_NAME], capture_output=True, text=True, cwd=WS)
    arm = subprocess.run(BQ + ['intend', INTENTION_TRIGGER, INTENTION_ACTION],
                         capture_output=True, text=True, cwd=WS)
    armed_ok = 'Armed' in (arm.stdout + arm.stderr) or 'Stored' in (arm.stdout + arm.stderr)
    try:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The battery mines queries from node names, properties, and content stored in the SQLite database, then persists failure artifacts containing query text and output snippets to JSONL files under logs. Because mined cases include emails and rare content terms, these logs can disclose sensitive workspace data to anyone with log access, and the script provides no warning, redaction, or opt-in before doing so.

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 · engine/engram-battery.py (reported line 213)May include surrounding context.

python
"""Returns (stdout, stderr, returncode, elapsed_seconds)."""
    t0 = time.time()
    try:
        r = subprocess.run(BQ + ['recall', query, '--compact', '--budget', '400'] + list(flags),
                           capture_output=True, text=True, timeout=60, cwd=WS)
        return (r.stdout or ''), (r.stderr or ''), r.returncode, time.time() - t0
    except subprocess.TimeoutExpired:

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 · engine/engram-maintenance.py (reported line 71)May include surrounding context.

python
def run_engram(args, capture=True):
    cmd = [sys.executable, ENGRAM] + args
    if capture:
        result = subprocess.run(cmd, capture_output=True, text=True, cwd=WORKSPACE)
        return result.stdout + result.stderr
    else:
        subprocess.run(cmd, cwd=WORKSPACE)

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 · engine/engram-maintenance.py (reported line 74)May include surrounding context.

python
result = subprocess.run(cmd, capture_output=True, text=True, cwd=WORKSPACE)
        return result.stdout + result.stderr
    else:
        subprocess.run(cmd, cwd=WORKSPACE)

def log_to_engram(message, reason):
    run_engram(['log', message, '--reason', reason])

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 · engine/engram-migrate.py (reported line 347)May include surrounding context.

python
]

        try:
            result = subprocess.run(
                cmd, capture_output=True, text=True, timeout=30,
                cwd=WORKSPACE,
            )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script sends extracted content, names, and source tags derived from MEMORY.md, memory/*.md, and AGENTS.md to another CLI using subprocess.run. While the top-level docstring says --apply stores nodes, there is no explicit runtime warning that potentially sensitive workspace memory content will be persisted externally through the Engram tool.

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 · engine/engram-migrate.py (reported line 362)May include surrounding context.

python
"--tags", tags_str,
                    "--merge",
                ]
                result2 = subprocess.run(
                    cmd_update, capture_output=True, text=True, timeout=30,
                    cwd=WORKSPACE,
                )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

This writes cron entries containing unquoted, workspace-derived paths directly into a crontab line. Even though subprocess.run is not using a shell here, cron will later execute the line via /bin/sh, so a malicious or specially crafted WORKSPACE or SCRIPTS_DIR containing shell metacharacters could lead to command injection or execution of unintended commands on a schedule.

Content

Scanner excerpt · engine/engram-rewire.py (reported line 186)May include surrounding context.

python
return 0

        new_crontab = current.rstrip('\n') + '\n' + '\n'.join(crons) + '\n'
        proc = subprocess.run(["crontab", "-"], input=new_crontab, capture_output=True, text=True)
        if proc.returncode == 0:
            print(f"  Added {len(crons)} cron entries")
            return len(crons)

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 · engine/engram-seed-battery.py (reported line 64)May include surrounding context.

python
"""
    cmd = [sys.executable, ENGRAM_CLI, "recall", query, "--limit", "5"]
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=60,
                           cwd=WORKSPACE)
        return r.stdout, r.stderr, r.returncode
    except subprocess.TimeoutExpired:

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
engine/engram-serve.py:199