Back to skill

Security audit

Insight Engine

Security checks for vulnerabilities and agentic risk

Overview

This reporting skill is mostly purpose-aligned, but it can send raw local memory and other sensitive operational data to configurable network services despite documentation implying raw logs stay local.

Review before installing. Use only with dedicated low-privilege Notion and Langfuse credentials, keep Langfuse and Ollama endpoints local or HTTPS-trusted, avoid enabling cron until you are comfortable with recurring external writes, and assume daily runs may transmit raw OpenClaw memory excerpts to the configured model service unless the skill is changed.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/engine.py:125
Finding

Raw Agent Memory Is Transmitted to Anthropic Contrary to the Documented Privacy Boundary

Content
View full analysis
Optional[str]: """Read the daily memory markdown file.""" if target_date is None: target_date = date.today() if memory_dir is None: memory_dir = os.environ.get( 'OPENCLAW_MEMORY_DIR', os.path.join(os.path.expanduser('~'), '.openclaw', 'memory') ) path = Path(memory_dir) / f'{target_date}.md' if path.exists(): return path.read_text(errors='replace') return None ``` ```python # scripts/src/engine.py:125-128 memory_text = read_memory_file( memory_dir=cfg['openclaw']['memory_dir'], target_date=target_date ) or '(no memory file for this date)' ``` ```python # scripts/src/engine.py:162-170 return { 'date': str(target_date), 'log_summary': log_summary, 'langfuse_today': langfuse_summary, 'langfuse_7day': langfuse_week, 'git_activity': git_summary, 'cp_scores': cp_scores, 'cp_trends': cp_trends, 'memory_context': memory_text[:6000], } ``` ```python # scripts/src/engine.py:231-235 user_content = ( f"Analyse this operational data and produce today's reflection:\n\n" f"```json\n{json.dumps(data, indent=2, default=str)}\n```" ) ``` ```python # scripts/src/engine.py:266 reflection = call_claude(system_prompt, user_content, model) ``` ### Technical Analysis The daily pipeline reads up to 6,000 characters from the Agent's daily memory file and inserts the text verbatim into the JSON packet submitted to Anthropic. No local redaction, secret detection, field-level filtering, or user confirmation occurs before transmission. This exceeds the ...[truncated 1627 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/src/engine.py:231
Finding

Untrusted Memory and Git Metadata Are Embedded in LLM Input Without Prompt-Injection Isolation

Content
View full analysis
1 else '' meta = subprocess.check_output( ['git', 'show', '--no-patch', '--format=%aI|%an', h], cwd=repo_path, stderr=subprocess.DEVNULL, text=True ).strip().splitlines()[0] ``` ```python # scripts/src/collectors/git_collector.py:68-78 commits.append({ 'hash': h[:8], 'subject': subject, 'body': body[:400] if body else '', 'author': author, 'timestamp': timestamp, 'files_changed': files_changed[:15], 'insertions': insertions, 'deletions': deletions, 'repo': Path(repo_path).name, }) ``` ```markdown # scripts/prompts/daily_analyst.md:32-35 You have access to the memory file — what actually happened, in plain English. Use it. Integrate the narrative with the numbers. "We found the model was scoring its own thinking tokens (see deepseek-r1 extract: 0.096)" is better than "extract score was 0.096 (anomalous)." ``` ```markdown # scripts/prompts/daily_analyst.md:46-53 - **log_summary**: OpenClaw gateway token usage and model calls - **langfuse_today / langfuse_7day**: Shadow eval traces, scores, observations - **git_activity**: What code actually changed today — commit subjects and file counts - * ...[truncated 2223 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/collectors/langfuse_data.py:20
Finding

Langfuse Basic Authentication Credentials Can Be Sent to an Arbitrary or Plaintext Endpoint

Content
View full analysis
tuple: pk = public_key or os.environ.get('LANGFUSE_PUBLIC_KEY', '') sk = secret_key or os.environ.get('LANGFUSE_SECRET_KEY', '') return (pk, sk) ``` ```python # scripts/src/collectors/langfuse_data.py:40-50 def fetch_traces(days_back: int = 1, base_url: str = 'http://localhost:3100', limit: int = 100, public_key: str = '', secret_key: str = '') -> list[dict]: resp = requests.get( f'{base_url}/api/public/traces', auth=_auth(public_key, secret_key), params={'limit': limit}, timeout=15 ) resp.raise_for_status() cutoff = _since_ts(days_back) return [ t for t in resp.json().get('data', []) if _parse_ts(t.get('timestamp') or t.get('createdAt')) >= cutoff ] ``` ```python # scripts/src/collectors/langfuse_data.py:56-79 def fetch_observations(days_back: int = 1, base_url: str = 'http://localhost:3100', limit: int = 100, public_key: str = '', secret_key: str = '') -> list[dict]: resp = requests.get( f'{base_url}/api/public/observations', auth=_auth(public_key, secret_key), params={'limit': limit, 'type': 'GENERATION'}, timeout=15 ) resp.raise_for_status() cutoff = _since_ts(days_back) return [ o for o in resp.json().get('data', []) if _parse_ts(o ...[truncated 2105 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/engine.py:98
Finding

Dry-Run Mode Can Send the Complete Sensitive Data Packet to a Remote Ollama Endpoint

Content
View full analysis
str: """Fallback: local Ollama for cost-free offline / dry-run use.""" import requests as req resp = req.post( f'{base_url}/api/chat', json={ 'model': model, 'messages': [ {'role': 'system', 'content': system_prompt}, {'role': 'user', 'content': user_content} ], 'stream': False }, timeout=120 ) resp.raise_for_status() return resp.json()['message']['content'] ``` ```python # scripts/src/engine.py:258-264 if dry_run: ollama_model = os.environ.get('OLLAMA_MODEL', 'mistral') ollama_url = os.environ.get('OLLAMA_BASE_URL', 'http://localhost:11434') print(f"[insight-engine] dry run — calling local Ollama ({ollama_model})...") reflection = call_ollama(system_prompt, user_content, model=ollama_model, base_url=ollama_url) print("\n" + "=" * 60) ``` ### Technical Analysis Dry-run mode is documented and logged as a local or offline operation, but `OLLAMA_BASE_URL` accepts an arbitrary URL. The complete analysis packet is posted to that endpoint, including raw daily memory during daily mode, operational statistics, Git metadata, Langfuse summaries, and control-plane information. No loopback validation, HTTPS requirement, destination confirmation, or remote-mode warning is implemented. Consequently, a configuration mistake or attacker-controlled environment can silently invalidate the user's expectation that dry-run data remains on the machine. ### Attack Path 1. `OLLAMA_BASE_URL` is confi ...[truncated 923 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:47
Finding

Runtime Dependencies Are Installed Without Version or Integrity Pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (40)

Tainted flow: 'base_url' from os.environ.get (line 25, credential/environment) → requests.get (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 · scripts/src/collectors/cp_trends.py (reported line 28)May include surrounding context.

python
base_url = api_url or os.environ.get('CP_API_URL', 'http://localhost:8765')

    try:
        r = requests.get(f'{base_url}/trends', timeout=timeout)
        r.raise_for_status()
        trends_data = r.json()

Tainted flow: 'base_url' from os.environ.get (line 25, credential/environment) → requests.get (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 · scripts/src/collectors/cp_trends.py (reported line 46)May include surrounding context.

python
})
        on_promotion_track.sort(key=lambda x: x['runs_to_promotion'])

        r2 = requests.get(f'{base_url}/history', params={'limit': 20}, timeout=timeout)
        r2.raise_for_status()
        history_data = r2.json()

Tainted flow: 'cp_api_url' from os.environ.get (line 151, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The request target is taken directly from the CP_API_URL environment variable and used in an outbound HTTP request with no allowlist or validation. In environments where an attacker can influence configuration, this creates an SSRF-style primitive that can redirect the engine to arbitrary internal or external hosts and leak request metadata or enable probing of internal services.

Content

Scanner excerpt · scripts/src/engine.py (reported line 154)May include surrounding context.

python
cp_api_url = os.environ.get('CP_API_URL', 'http://localhost:8765')
    try:
        import requests as req
        r = req.get(f'{cp_api_url}/status', timeout=5)
        if r.status_code == 200:
            cp_scores = r.json().get('confidence', {})
    except Exception:

Tainted flow: 'existing_id' from os.environ.get (line 88, credential/environment) → requests.patch (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 · scripts/src/writers/notion_writer.py (reported line 90)May include surrounding context.

python
existing_id = _find_existing_page(db_id, title)
    if existing_id:
        requests.patch(
            f'https://api.notion.com/v1/pages/{existing_id}',
            headers=_headers(),
            json={'properties': {'Notes': {'rich_text': [{'text': {'content': content[:2000]}}]}}},

Tainted flow: 'existing_id' from os.environ.get (line 88, credential/environment) → requests.patch (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 · scripts/src/writers/notion_writer.py (reported line 101)May include surrounding context.

python
{'object': 'block', 'type': 'heading_2',
             'heading_2': {'rich_text': [{'type': 'text', 'text': {'content': f'Re-run — {date.today()}'}}]}},
        ] + blocks
        requests.patch(
            f'https://api.notion.com/v1/blocks/{existing_id}/children',
            headers=_headers(),
            json={'children': rerun_blocks[:50]},

Tainted flow: 'root_page_id' from os.environ.get (line 136, credential/environment) → requests.post (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 · scripts/src/writers/notion_writer.py (reported line 163)May include surrounding context.

python
# Create it
    db_title = f'Insight Engine — {period.title()} Reflections'
    resp = requests.post(
        'https://api.notion.com/v1/databases',
        headers=_headers(),
        json={

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill is presented as an operational log-insight pipeline, but the referenced behavior includes reading local git repositories and invoking git subprocesses to extract commit metadata, which is materially different from the stated purpose. That discrepancy can expose source-code history, author metadata, and repository contents under the guise of log analysis, creating a meaningful risk of unintended data access and trust-boundary bypass.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as an operational log-insight pipeline, but the referenced behavior includes reading local git repositories and invoking git subprocesses to extract commit metadata, which is materially different from the stated purpose. That discrepancy can expose source-code history, author metadata, and repository contents under the guise of log analysis, creating a meaningful risk of unintended data access and trust-boundary bypass.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises capabilities that include shell, file reads, environment access, and outbound network use, but it does not declare an explicit tool scope such as permissions or allowed-tools. That makes the effective execution surface ambiguous and increases the chance that an agent may grant broader access than intended, especially given the presence of API keys, local file access, and external writes described in the metadata.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

Cron setup (LaunchAgent example)

xml
<!-- ~/Library/LaunchAgents/com.yourname.insight-engine-daily.plist -->
<key>StartCalendarInterval</key>
<dict>
  <key>Hour</key><integer>23</integer>

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This file introduces collection of commit history from arbitrary local repositories, which is outside the manifest's stated scope of analyzing AI system logs and traces. In a skill context, unexpected access to local development repositories is dangerous because it can silently surface sensitive project names, commit contents, author information, and change summaries into downstream LLM or reporting pipelines.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The collector gathers commit subjects, bodies, authors, timestamps, and file names without any user-facing warning beyond internal comments. That is risky because commit metadata often contains sensitive internal details, and in this skill's broader pipeline such data could be forwarded into summaries, LLM prompts, or external report destinations without the user's informed awareness.

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/src/collectors/git_collector.py (reported line 34)May include surrounding context.

python
return []

    try:
        hashes = subprocess.check_output(
            ['git', 'log', f'--since={since}', f'--until={until}',
             '--format=%H', '--no-merges'],
            cwd=repo_path, stderr=subprocess.DEVNULL, text=True

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill executes the local git binary to inspect arbitrary repositories even though the declared skill purpose is operational insights from logs, metrics, traces, and gateway data. That expands the capability surface to local source-control harvesting, which can expose sensitive commit messages, author identities, and code-change metadata from unrelated projects when environment variables or defaults point at available repos.

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/src/collectors/git_collector.py (reported line 45)May include surrounding context.

python
commits = []
    for h in hashes:
        try:
            msg = subprocess.check_output(
                ['git', 'show', '--no-patch', '--format=%s%n%b', h],
                cwd=repo_path, stderr=subprocess.DEVNULL, text=True
            ).strip()

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/src/collectors/git_collector.py (reported line 53)May include surrounding context.

python
subject = lines[0] if lines else ''
            body = '\n'.join(lines[1:]).strip() if len(lines) > 1 else ''

            meta = subprocess.check_output(
                ['git', 'show', '--no-patch', '--format=%aI|%an', h],
                cwd=repo_path, stderr=subprocess.DEVNULL, text=True
            ).strip().splitlines()[0]

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/src/collectors/git_collector.py (reported line 59)May include surrounding context.

python
).strip().splitlines()[0]
            timestamp, author = meta.split('|', 1) if '|' in meta else (meta, 'unknown')

            stat = subprocess.check_output(
                ['git', 'show', '--stat', '--format=', h],
                cwd=repo_path, stderr=subprocess.DEVNULL, text=True
            ).strip()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code performs outbound REST API calls to retrieve Langfuse traces, observations, and scores, which can contain user or system telemetry. Although the module docstring describes the collector and required environment variables, there is no user-facing print/log/confirmation at the call sites to disclose that network access and data retrieval are occurring.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This code reads daily memory markdown files from the user's home directory and gateway logs from configurable paths, which may contain sensitive user or system data. Although the module docstring documents the environment variables, there is no user-facing warning, confirmation, or explicit disclosure that the skill accesses potentially sensitive local logs and memory files.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/src/engine.py (reported line 5)May include surrounding context.

python
#!/usr/bin/env python3
"""
Insight Engine — main orchestrator.
Collect → Analyse → Reflect → Write to Notion.

Usage:
  python3 engine.py --mode daily

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The manifest describes a pipeline focused on AI system logs, Langfuse traces, gateway logs, Python statistics, LLM interpretation, and Notion reporting. Importing and later using a git collector adds repository-activity surveillance that is not mentioned as part of the skill's purpose and is not an obvious requirement for operational log analysis.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code sends aggregated operational data, including memory context and service summaries, to Anthropic without any inline consent gate, redaction step, or clear disclosure at execution time. This can expose sensitive operational data to a third-party model provider, especially because the data packet is broad and built automatically.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

In dry-run mode, the code posts the full prompt and user content to the Ollama chat API, which still transmits collected data over HTTP to another service. The code logs that Ollama is being called, but it does not clearly disclose that logs, memory context, and other aggregated operational data are included in that request.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The daily packet includes up to 6000 characters from a local memory file and then embeds that content into the LLM prompt. If the memory store contains secrets, internal notes, user data, or other sensitive context, that information can be disclosed to the model provider and may also be reproduced in generated reflections or written onward to Notion.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The engine reaches out to an undeclared control-plane API and incorporates its returned data into the reporting pipeline. Because this extra network dependency is both externally configurable and not disclosed by the stated skill scope, it increases attack surface and can quietly import untrusted or sensitive data into downstream prompts and reports.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.