Back to skill

Security audit

SkillChain

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its supply-chain analysis purpose, but it needs review because it broadly indexes local skills and automatically loads code from a sibling ontology skill without integrity checks.

Install only if you are comfortable with a skill that inventories other local skills and stores a local graph of their metadata. Run scan and enrich deliberately, avoid using enrich if you do not want installed skill slugs queried against clawhub.ai, and be cautious if an ontology sibling skill is installed because this package will automatically import and execute that sibling Python module.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T08 · Insecure Dependencies

Warning
Location
scripts/ingest.py:67
Finding
Unverified sibling ontology module execution during ingestion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ingest.py`, lines 67–82 **Vulnerability Type**: Unverified local dependency loading **Risk Level**: Medium ### Vulnerable Code ```python _ONTOLOGY_SCRIPT = Path(__file__).resolve().parent.parent.parent / "ontology" / "scripts" / "ontology.py" if _ONTOLOGY_SCRIPT.exists(): sys.path.insert(0, str(_ONTOLOGY_SCRIPT.parent)) try: from ontology import ( load_graph, create_entity, create_relation, update_entity, append_op, generate_id, ) _ONTOLOGY_OK = True except ImportError: _ONTOLOGY_OK = False ``` ### Technical Analysis The ingestion script constructs a path to a separate sibling skill, adds that directory to the beginning of `sys.path`, and imports the `ontology` module solely because the expected file exists. Python executes a module's top-level code during import. Consequently, the imported sibling component receives code execution before the requested ingestion operation begins. The implementation does not validate the module's cryptographic digest, provenance, ownership, filesystem permissions, or trusted installation source. Because the directory is inserted at index zero in `sys.path`, it takes precedence over normal module search locations. A malicious or compromised sibling `ontology` skill can therefore supply attacker-controlled implementations or arbitrary import-time code. The script already contains local fallback graph functions, so execution of an external sibling module is not required for the core ingestion workflow. ### Attack Path 1. An attacker publishes or distributes a malicious skill under the sibling name `ontology`, or compromises an existing installation. 2. The malicious package places attacker-controlled code at `ontology/scripts/ontology.py`. 3. The victim installs that component alongside this skill. 4. The victim runs a documented command such as: ```bash python3 scripts/ingest.py scan ...[truncated 1180 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the implicit sibling-module import and use the bundled fallback implementation unconditionally when possible. 2. If shared functionality is necessary, package it as an explicit, version-pinned dependency installed from a trusted source. 3. Verify the dependency with a cryptographic hash or signed package metadata before loading it. 4. Avoid inserting automatically discovered directories at the beginning of `sys.path`. 5. Require an explicit configuration option for any external ontology implementation. 6. Resolve and validate the configured path before import, including: - Ensuring it is inside an approved installation root. - Rejecting unexpected symbolic-link traversal. - Checking ownership and write permissions. - Rejecting files writable by untrusted users. 7. Prefer importing through standard package management rather than loading code based only on filesystem presence. 8. Run ingestion with minimal filesystem, environment, subprocess, and network privileges as defense in depth. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/analyze.py:27
Finding
Unverified sibling ontology module execution during analysis<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze.py`, lines 27–34 **Vulnerability Type**: Unverified local dependency loading **Risk Level**: Medium ### Vulnerable Code ```python _ONTOLOGY_SCRIPT = Path(__file__).resolve().parent.parent.parent / "ontology" / "scripts" / "ontology.py" if _ONTOLOGY_SCRIPT.exists(): sys.path.insert(0, str(_ONTOLOGY_SCRIPT.parent)) try: from ontology import load_graph _load = load_graph except ImportError: ``` ### Technical Analysis The analysis entry point automatically trusts a Python module located in a separate sibling skill. If the expected file exists, its directory is placed first in `sys.path`, after which `ontology` is imported. Importing a Python module executes all of its top-level statements. The imported module is not authenticated or checked against a trusted digest, and its ownership, permissions, and provenance are not evaluated. As a result, invoking any analysis subcommand can execute code supplied by a malicious or compromised sibling component. Catching `ImportError` does not mitigate this risk. The exception handler only supports cases where importing fails; attacker-controlled code executes before the import returns successfully and can perform arbitrary operations without raising `ImportError`. The file contains a self-contained graph-loading fallback, making automatic execution of the external module unnecessary for basic analysis. ### Attack Path 1. An attacker causes a malicious or modified `ontology/scripts/ontology.py` to be present in the expected sibling directory. 2. The victim invokes any documented analysis command, for example: ```bash python3 scripts/analyze.py report ``` 3. The script detects the sibling module and inserts its directory at the front of `sys.path`. 4. The `from ontology import load_graph` statement executes the module's top-level code. 5. The attacker's payload runs with the same authority as the analysis process. 6. The payload ...[truncated 980 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the local graph loader by default and remove automatic discovery of sibling Python code. 2. Move shared graph functionality into a formally packaged dependency with an exact version and integrity lock. 3. Authenticate any externally loaded implementation using a trusted hash or signature. 4. Do not prepend an automatically discovered directory to `sys.path`. 5. If plugin behavior is required, require explicit administrator or user configuration and display the exact module path before loading it. 6. Validate resolved paths, ownership, permissions, and symbolic links against a trusted installation root. 7. Execute optional plugins in a separate, restricted process with only the graph file access needed for the task. 8. Treat imported graph data as untrusted and validate entity and relation structures before using them to generate reports. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
Findings (22)

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
python3 scripts/ingest.py analyze-all
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `SKILL.md` frontmatter | name, description, license, `allowed-tools`, `metadata.requires.bins`, `read_when` | Yes |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `SKILL.md` frontmatter | name, description, license, `allowed-tools`, `metadata.requires.bins`, `read_when` | Yes |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill documentation describes capabilities that imply file access, graph writes, shell execution, and optional network enrichment, but the frontmatter does not declare any explicit tool scope or permissions. This creates a transparency and least-privilege problem: an agent may invoke a skill whose effective capabilities are broader than what the manifest signals, increasing the chance of unsafe execution or over-privileged use.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The trigger phrase "Analyze my skills" is broad and likely to overlap with ordinary user requests, which can cause the skill to activate in situations the user did not specifically intend. In this skill's context, activation leads to local skill ecosystem scanning and optional enrichment behavior, so accidental invocation could expose inventory details or cause unnecessary analysis actions.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documented reset workflow rebuilds storage and references append-only graph data, but it does not prominently warn that reset may delete or overwrite previously collected graph state. A user or agent following the one-shot or reset instructions could unintentionally destroy local analysis data, making this a real integrity and availability risk.

Skill Enumeration

Medium
Category
Agent Snooping
Content
python3 scripts/ingest.py scan

# Specify directories explicitly
python3 scripts/ingest.py scan --dirs ~/Downloads/skills-main/skills ~/.codex/skills ~/Downloads/ontology

# Online enrichment: adds stars, downloads, moderation verdict from clawhub
# Skipped automatically if network is unavailable
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
"""Parse pyproject.toml: try tomllib/tomli, fall back to regex."""
    for mod in ("tomllib", "tomli"):
        try:
            lib = __import__(mod)
            data = lib.loads(text)
            _parse_pyproject_data(data, add_fn)
            return
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
ts = datetime.now(timezone.utc).isoformat()
        rec = {"op": "update", "id": eid, "properties": props, "timestamp": ts}
        _append_op_fallback(GRAPH_PATH, rec) if not _ONTOLOGY_OK else \
            __import__("ontology").append_op(GRAPH_PATH, rec)
        entities[eid]["properties"].update(props)
        return eid
    entity = _mkent("Skill", {**props, "slug": slug}, GRAPH_PATH)
Confidence
89% confidence
Finding
This dynamically imports ontology after prepending a filesystem path derived from a relative location outside the current skill directory, then invokes append_op on the imported module. If an attacker can place or replace an ontology.py in that searched location, they can gain code execution when this script runs, making this a local module-hijacking risk tied to import-path manipulation.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The manifest emphasizes local ecosystem analysis, dependency discovery, security posture, and inventory of installed skills. This file adds a network-backed enrichment capability that fetches remote metadata from clawhub.ai, which is broader than purely local analysis and is not reflected in the stated description.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
A supply-chain intelligence skill is expected to read local manifests and files, but spawning `npm root -g` is a separate capability that executes an external program on the host. The manifest does not mention shelling out or requiring command execution as part of the skill's scope.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Global npm skills directory: $(npm root -g)/openclaw/skills
    try:
        proc = subprocess.run(
            ["npm", "root", "-g"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The reset command unconditionally clears the graph file by writing an empty string, which is a destructive operation that removes stored data. The code provides a post-action print message but no prior confirmation prompt or explicit warning in the command implementation before deletion occurs.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("=" * 60)
        print(step)
        print("=" * 60)
        _sp.run([_sys.executable, str(analyze), subcmd], check=False)


# ---------------------------------------------------------------------------
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Low
Confidence
82% confidence
Finding
This code writes generated content to an arbitrary path supplied by `--out`, which is a file-modifying operation. Although the CLI help mentions writing a report, there is no confirmation prompt or explicit runtime disclosure before overwriting/creating the target file.

Static analysis

No suspicious patterns detected.