Back to skill

Security audit

Fact Checker

Security checks for vulnerabilities and agentic risk

Overview

The skill appears intended to fact-check drafts locally, but it automatically reads broad persistent memory and contacts a localhost API without strong scoping or opt-in controls.

Review this skill before installing if your workspace memory logs may contain private or unrelated information. It does not show exfiltration or destructive behavior, but it should be treated as a local fact-checking tool that can read broad workspace evidence and query a localhost service automatically.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/fact_check.py:46
Finding

Unnecessarily Broad Access to Persistent Agent Memory

Content
View full analysis

Vulnerability Details

File Location: scripts/fact_check.py:46-54, 414-419
Vulnerability Type: Excessive access to persistent Agent state
Risk Level: Medium

Vulnerable Code

python
def load_memory_logs() -> str:
    """Load all memory/*.md files into a single string."""
    if not MEMORY_DIR.exists():
        return ""
    return "\n\n".join(
        f"### {f.name}\n{f.read_text()}"
        for f in sorted(MEMORY_DIR.glob("*.md"))
    )

The collection occurs unconditionally during every fact-check:

python
# Load sources
findings_text = load_findings()
changelog_text = load_changelog()
memory_text = load_memory_logs()
score_files = load_score_files()
status_data = fetch_status_api()
git_log_text = load_git_log()

Technical Analysis

The implementation reads every Markdown file in the workspace-wide memory/ directory and combines the full contents into one string. This occurs for every draft, even when the draft contains no date or numeric claims that require memory-based verification.

Persistent Agent memory can contain unrelated conversation history, decisions, identifiers, operational details, or sensitive information. A fact-checking task ordinarily needs access only to explicitly selected evidence or records relevant to the claims being checked. Reading all memory files violates least-privilege and data-minimization principles.

The audited implementation does not transmit the loaded memory to an external service. However, it systematically places unrelated persistent state into the process's accessible data set. Matching snippets may also influence report evidence. This expands the exposure scope if the process, its output destination, or a future modification is compromised.

Attack Path

  1. A user invokes the Skill to check a Markdown draft.
  2. run_fact_check() calls load_memory_logs() without requiring consent, an opt-in ...[truncated 1093 chars]
Remediation
View remediation

Remediation Suggestions

  1. Disable memory access by default and require an explicit option such as --include-memory.
  2. Accept specific evidence file paths rather than scanning the entire memory directory.
  3. Load memory only after extraction confirms that a claim type genuinely requires it.
  4. Restrict date-related searches to memory files corresponding to the claimed date or an explicitly bounded date range.
  5. Resolve and validate every selected path against an approved evidence root before reading it.
  6. Search files incrementally and retain only minimal matching excerpts instead of aggregating full contents.
  7. Clearly disclose memory access in the Skill documentation and obtain user approval before accessing persistent state.
  8. Add tests proving that memory files are not read unless the user explicitly enables that source.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/fact_check.py:57
Finding

Network Capability Declaration Conflicts with Loopback HTTP Request

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:23-28; scripts/fact_check.py:30, 57-64, 418
Vulnerability Type: Inaccurate security metadata and insufficiently validated local API trust boundary
Risk Level: Low

Vulnerable Code

The Skill declares that it does not use outbound networking:

yaml
metadata:
  openclaw:
    emoji: "🔍"
    network:
      outbound: false
      reason: "All verification against local files and APIs only"
    subprocess:
      note: "Runs python3 scripts for verification — legitimate use"
    security_notes: "All operations are performed locally. No data leaves the user's machine. Verification cross-references local files, local git history, and a local API (localhost). The subprocess note is for running the bundled Python fact-check script — not arbitrary code execution."

The implementation nevertheless performs an HTTP request through the network stack:

python
STATUS_API = "http://localhost:8765/status"
python
def fetch_status_api() -> Optional[dict]:
    """Fetch live data from Control Plane /status API.  Returns None on failure."""
    try:
        resp = requests.get(STATUS_API, timeout=5)
        resp.raise_for_status()
        return resp.json()
    except Exception:
        return None

The request is made unconditionally during source loading:

python
status_data = fetch_status_api()

Technical Analysis

The fixed destination is the loopback address localhost:8765, and the request does not include draft contents, memory contents, credentials, environment variables, or user-supplied query parameters. Therefore, the observed code does not establish sensitive-information exfiltration.

However, localhost HTTP still constitutes network activity. Declaring outbound: false conflicts with the implementation and can undermine capability review or sandbox policy enforcement. The ...[truncated 1786 chars]

Remediation
View remediation

Remediation Suggestions

  1. Update the Skill metadata to declare loopback HTTP access accurately, including the fixed host, port, endpoint, and purpose.
  2. Alternatively, remove the API request and rely only on local files if the Skill must remain network-free.
  3. Make live API access opt-in through a flag such as --live-status.
  4. Validate that parsed responses are dictionaries with the expected confidence and cost_today structures.
  5. Validate all numeric fields before arithmetic or report formatting.
  6. Enforce a maximum response size and reject unexpected content types.
  7. If supported by the local service, authenticate the endpoint or use a protected Unix-domain socket to reduce local spoofing risk.
  8. Add tests covering malformed JSON, oversized responses, missing fields, incorrect field types, and attacker-controlled status values.
  9. Document the third-party requests dependency and pin it through the project's dependency-management mechanism.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Lp1

High
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The script invokes git through subprocess, giving it command-execution capability that is broader than simple markdown analysis. In a skill framework, undeclared execution capabilities are risky because they can interact with the environment and repository state in ways the caller may not anticipate.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The script invokes git through subprocess, giving it command-execution capability that is broader than simple markdown analysis. In a skill framework, undeclared execution capabilities are risky because they can interact with the environment and repository state in ways the caller may not anticipate.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The script invokes git through subprocess, giving it command-execution capability that is broader than simple markdown analysis. In a skill framework, undeclared execution capabilities are risky because they can interact with the environment and repository state in ways the caller may not anticipate.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The metadata says outbound: false and that all operations are local with 'No data leaves the user's machine', but it also says verification is done against 'APIs' and specifically a 'local API (localhost)'. That is an active contradiction in the documentation of the skill's behavior and network posture, even if the API is only on localhost.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The instruction says to use the skill when asked to "fact-check" or "verify" a draft blog post, report, or documentation file. Phrases like "verify" are broad and common in everyday requests, and the file does not provide exclusion conditions or more specific trigger constraints to distinguish this skill from other review or editing tasks.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest describes a fact-checking skill that verifies drafts against known sources. While consulting git history is declared in the broader use context, implementing that by spawning a subprocess introduces an execution capability beyond what is normally justified for a verifier and is not necessary when other read-only data sources are already used.

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/fact_check.py (reported line 89)May include surrounding context.

python
if not CP_PROJECT.exists():
        return ""
    try:
        result = subprocess.run(
            ["git", "log", "--oneline", "--date=short", "--format=%h %ad %s", "-50"],
            cwd=str(CP_PROJECT),
            capture_output=True,

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/test_fact_check.py (reported line 391)May include surrounding context.

python
if __name__ == "__main__":
    loader = unittest.TestLoader()
    suite = loader.loadTestsFromModule(__import__(__name__))
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    sys.exit(0 if result.wasSuccessful() else 1)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script performs network access to a local HTTP endpoint automatically during analysis without making that side effect obvious to the user at invocation time. In a security-sensitive agent setting, silent network activity is dangerous because localhost services often expose privileged operational data and users may not expect draft review to probe them.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest and module description emphasize verifying claims in markdown drafts against source data. The CLI additionally supports writing a generated report to an arbitrary user-supplied path, which is broader than pure verification and adds a side-effecting file-write behavior not reflected in the stated description.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

This code performs a file write operation when --output is provided, but there is no confirmation prompt and no explicit warning in comments or help text that an existing file may be overwritten. For code-file SQP-2, file writes can merit a finding when they lack user disclosure and the risk is not clearly explained.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.