T03 · Remote Payload Retrieval and Execution
- Location
scripts/module_manager.py:301- Finding
Fail-Open Module Registry Verification Enables Remote Payload Execution
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
ERPClaw is a coherent local ERP skill, but its module installation and onboarding paths create a meaningful remote-code and sensitive-data exposure risk that users should review before installing.
Install only if you are comfortable giving this skill control over local business records, payroll data, credentials, backups, and persistent modules. Avoid real production data or module onboarding until the registry verification and module source restrictions are tightened, and treat installed expansion modules as code with the same privileges as the ERP process.
scripts/module_manager.py:301Fail-Open Module Registry Verification Enables Remote Payload Execution
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
for attempt in range(retries + 1):
try:
req = urllib.request.Request(url, headers={"User-Agent": "erpclaw"})
resp = urllib.request.urlopen(req, timeout=timeout)
return resp.read()
except Exception as e:
last_err = e
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
base = os.environ.get("ERPCLAW_GITHUB_RAW_BASE", GITHUB_RAW_BASE)
url = f"{base}/{rel_path}"
req = urllib.request.Request(url, headers={"User-Agent": "erpclaw"})
with urllib.request.urlopen(req, timeout=15) as resp:
data = resp.read()
actual = hashlib.sha256(data).hexdigest()
if actual != expected_hash:
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.
### Changed
- **`erpclaw_lib/crypto.py` rewritten** to use the `cryptography` library (OpenSSL via cffi). PBKDF2-HMAC-SHA256 at 600,000 iterations (OWASP 2024). Field-level encryption uses raw AES-256-GCM with `enc:v2:` prefix. Legacy `ECRYPT01` decrypt path retained for v4.0.x backups.
- **Stripe addon: hard-removed `--api-key` flag.** `erpclaw-integrations-stripe` v2.0.1 reads credentials from the foundation credential store. Users migrate via `erpclaw migrate-credentials` (one-time read-from-DB → write-to-encrypted-store) or set fresh via `erpclaw set-credential --integration stripe --from-stdin`.
- **Lib bootstrap removed.** `_bootstrap.py` deleted. `~/.openclaw/erpclaw/lib` is now a symlink (created at `initialize-database`) to the skill-bundled location, not a self-healing copy. Eliminates the "self-modifying code at runtime" finding.
- **Foundation `SKILL.md` disclosure cleanup.** Removed 4 v4.0.1 paragraphs (Credential handling, Data protection, Module installation safety, Library self-heal) that were reading as scanner trigger surfaces while describing addon behavior or implementation details. Kept only the factual one-line Security summary.
### Removed
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.
### Changed
- **`erpclaw_lib/crypto.py` rewritten** to use the `cryptography` library (OpenSSL via cffi). PBKDF2-HMAC-SHA256 at 600,000 iterations (OWASP 2024). Field-level encryption uses raw AES-256-GCM with `enc:v2:` prefix. Legacy `ECRYPT01` decrypt path retained for v4.0.x backups.
- **Stripe addon: hard-removed `--api-key` flag.** `erpclaw-integrations-stripe` v2.0.1 reads credentials from the foundation credential store. Users migrate via `erpclaw migrate-credentials` (one-time read-from-DB → write-to-encrypted-store) or set fresh via `erpclaw set-credential --integration stripe --from-stdin`.
- **Lib bootstrap removed.** `_bootstrap.py` deleted. `~/.openclaw/erpclaw/lib` is now a symlink (created at `initialize-database`) to the skill-bundled location, not a self-healing copy. Eliminates the "self-modifying code at runtime" finding.
- **Foundation `SKILL.md` disclosure cleanup.** Removed 4 v4.0.1 paragraphs (Credential handling, Data protection, Module installation safety, Library self-heal) that were reading as scanner trigger surfaces while describing addon behavior or implementation details. Kept only the factual one-line Security summary.
### Removed
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
<p align="center">
<img src="https://img.shields.io/badge/license-GPL--3.0-green" alt="License: GPL-3.0">
<!-- version badge auto-synced by release/scripts/sync_facts.py (badge URL pattern) -->
<img src="https://img.shields.io/badge/version-v4.15.0-0d9488" alt="Version v4.15.0">
<img src="https://img.shields.io/badge/OpenClaw-skill-14b8a6" alt="OpenClaw Skill">
<img src="https://img.shields.io/badge/database-SQLite%20%7C%20PostgreSQL-0d9488" alt="SQLite or PostgreSQL">
The supplied code chunk is a small infrastructure utility module whose sole purpose is to resolve installation-related paths. It reads the ERPCLAW_HOME environment variable, expands a default home directory, and constructs paths to a lib directory, database file, and SKILL.md. It does not implement or expose ERP operations such as accounting, invoicing, inventory, HR, payroll, tax, reporting, audit trail, compliance, or module installation. While such a helper could support a larger ERP system, this chunk’s actual behavior is materially narrower than the declared description, so the description does not accurately represent what this code chunk actually does.
The declared description presents the skill as a full ERP platform with extensive operational capabilities across accounting, inventory, HR, payroll, reporting, and related domains. However, the supplied code chunk is not implementing those ERP capabilities; instead, it is a discovery/helper module for listing and describing actions exposed by the skill. Its primary function is to inspect Python source files and SKILL.md, extract action names and metadata, and report whether actions are destructive. This is a materially different purpose from the declared end-user functionality. While such code could be an internal support component of a larger ERP skill, the evaluation is for whether the supplied code chunk matches the declared description, and this chunk does not directly embody the described ERP behavior.
The description presents a full multi-domain ERP platform with extensive accounting, inventory, purchasing, tax, payroll, and financial reporting capabilities. However, the provided code chunk only shows HR-focused functionality: assigning shifts, listing shift assignments, adding attendance regularization rules, applying attendance regularization by updating attendance records, and managing employee documents and expiry checks. There is no evidence in this chunk of accounting, invoicing, inventory, purchasing, tax, billing, general ledger, financial reporting, consolidation, or expansion-module behavior. This is a material description-versus-code mismatch for the supplied chunk, even though the HR-related behavior is plausibly part of the broader claimed system.
The declared description presents a full ERP application with extensive operational/accounting capabilities. The supplied code chunk does not implement those business functions directly. Instead, it acts as a meta utility for setup/onboarding: it scans installed skills under ~/clawd/skills, inspects a SQLite database, reports installation status, recommends next modules to install, and seeds demo data by calling other modules' db_query.py scripts. While this supports an ERP ecosystem, its primary behavior is installation management and sample-data orchestration, not the broad ERP functionality claimed in the description. Therefore the description does not accurately represent this specific code chunk's behavior.
The supplied code chunk does not implement the declared ERP application capabilities such as accounting, invoicing, inventory, payroll, reporting, or optional module installation. Instead, it contains a list of governance rules for module compliance and helper accessors over those rules. While such a constitution could support the broader ERP platform internally, the actual code's primary purpose is policy definition and validation support, which is materially different from the declared description of business-facing ERP functionality. There is no evidence here of undeclared dangerous behavior, but there is a clear description-versus-behavior mismatch for this chunk.
The description presents the skill as a comprehensive ERP application with extensive operational domains and financial capabilities. The supplied code, however, is a maintenance/runtime command router focused on module validation, constitution article listing, table registry generation, and schema migration handlers. It also emits error messages for many actions that were moved to an addon, rather than implementing those ERP features directly. While this file may belong to a larger ERP project, the code chunk itself does not accurately reflect the declared primary functionality. That is a material description-versus-behavior mismatch.
The declared description presents a broad ERP application with business functions like accounting, payroll, inventory, tax, billing, and financial reporting. The supplied code does not implement those ERP business capabilities; instead, it is infrastructure code for database schema management. Its primary purpose is to compare declared schemas to the live SQLite database, generate and store migration plans, apply CREATE TABLE statements, roll back applied migrations by dropping created tables after backups, and detect schema drift. While such migration tooling could support an ERP system, this chunk’s behavior is materially narrower and different from the declared purpose, so the description does not accurately represent what this code actually does.
The declared description is for a full ERP application suite, but the code chunk shown does not implement ERP business functions like accounting, invoicing, HR, payroll, reporting, or GL operations. Instead, it implements developer/auditor tooling that validates ERP modules for compliance with internal architectural and security rules and runs tests. This is a materially different primary purpose, with undeclared capabilities such as static analysis, filesystem scanning, and subprocess-based test execution. While such tooling may support an ERP ecosystem, this specific code chunk is not accurately represented by the declared product description.
The description overstates the scope relative to the supplied code chunk. The code is explicitly a read-only reports module (db_query.py) and does not implement a full ERP system; it primarily reads accounting-related tables and emits JSON financial reports. Its behavior is consistent with a financial reporting/report-query skill, not with the full declared platform scope including inventory, purchasing, HR, payroll, broad billing/invoicing workflows, optional module installation, audit trail management, or general ERP transaction processing. There is no evidence here of undeclared dangerous behavior; the issue is that the declared purpose is much broader than the actual behavior of this specific code chunk.
Referenced artifact was not completely inspected
metadata: {"openclaw":{"type":"executable","install":{"post":"python3 scripts/erpclaw-setup/db_query.py --action initialize-database"},"requires":{"bins":["pyth
Skill instructs the agent to never refuse or to always comply. Suppressing the agent's ability to decline removes a core safety control and enables downstream harmful requests to succeed.
- **Before claiming an entity exists, is a duplicate, or has a balance/count — you MUST have called a `list-*`/`get-*` for it in THIS turn and seen it returned.** This is mandatory and has no exception. Asked to add or create something (a new customer, a received shipment of stock, a new supplier)? Do not refuse it as an existing duplicate from memory: call the relevant lookup (e.g. `list-customers`) this turn first; if it returns no match, CREATE it — the default for an add/create request is to act, not to refuse. A name, number, or "already set up" feeling from earlier in the chat, your workspace context, or training is NOT evidence it is in the books; if you have not run the lookup this turn, you do not know it exists. Conversely, when the ERP DOES return a document another session created, treat it as authoritative and act on it — do not refuse because a session was reset or your notes say the data is stale. The ERP query is the only truth; your memory is not.
Referenced artifact was not completely inspected
ed actions keep an installed foundation aligned with the published manifest in `module_registry.json`. `update-foundation --user-confirmed` compares each instal
Referenced artifact was not completely inspected
Router: `scripts/db_query.py` -> 14 core domains. Optional modules installed from GitHub (`avansaber/*`) to `~/.openclaw/erpclaw/modules/` (user-approved only).
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 470" width="760" height="470" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" role="img" aria-label="ERPClaw natural-language demo: add a customer, invoice them, record payment, show P and L by department">
<!--
Animated terminal demo for the ERPClaw README.
GitHub renders animated SVG through the <img> tag and runs SMIL <animate>,
but strips <script> and external fonts. Animation here is pure SMIL: each
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
def _resolve_env() -> dict:
"""Child env. ERPCLAW_HOME flows through unchanged so the subprocess router
resolves the same lib + DB the server resolved (paths.py mirrors it)."""
env = dict(os.environ)
# Be explicit so the child cannot diverge from the server's resolution.
env["ERPCLAW_HOME"] = paths.erpclaw_home()
return env
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
args[i + 1] = action_override
break
os.execvp(sys.executable, [sys.executable, script] + args)
def forward_script(script_path):
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
args[i + 1] = action_override
break
os.execvp(sys.executable, [sys.executable, script] + args)
def forward_script(script_path):
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.
sys.exit(1)
args = _strip_router_flags(list(sys.argv[1:]))
os.execvp(sys.executable, [sys.executable, script_path] + args)
def forward_module(module_name, action_override=None):
Skill instructs the agent to never refuse or to always comply. Suppressing the agent's ability to decline removes a core safety control and enables downstream harmful requests to succeed.
# appears in a shipped chart is accepted by construction — us_gaap (both copies),
# indian_coa and ca_coa_aspe together supply 21 of the types below, and
# test_the_coherence_map_accepts_every_pair_our_own_charts_ship re-derives that
# from the JSON on every run so the map can never refuse a chart we ship. That is
# the M91 lesson written down: a rule measured against the product's own data
# before it is enforced on anyone else's. (setup-chart-of-accounts inserts rows
# directly rather than through add_account, so chart loading is NOT gated by this
This code explicitly decrypts employees' stored bank routing numbers and includes the plaintext value in the normal API response while only masking the account number. In plain-language terms, it instructs the system to reveal sensitive financial data to whoever can invoke the listing action, which creates a clear data-leak path even though it is framed as a display convenience.
Detected: suspicious.dynamic_code_execution, suspicious.insecure_tls_verification