Back to skill

Security audit

Liudao Heritage

Security checks for vulnerabilities and agentic risk

Overview

This genealogy skill is purpose-aligned, but it needs Review because it can read and modify private family records using weakly scoped identity and unaudited local code paths.

Install only if you trust the local liudao-bot workspace and database operators, and only run it in an environment where access to private family records is intended. Before using it for personal data, require authenticated viewer identity, explicit confirmation for database writes, and a reviewed implementation of db_manager and relation_engine.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/search_person.py:20
Finding

Caller-Controlled Viewer Identity Used for Access Control

Content
View full analysis

Vulnerability Details

File Location: scripts/search_person.py:20-27 and SKILL.md:18-25
Vulnerability Type: Authentication and authorization bypass
Risk Level: High

Vulnerable Code

scripts/search_person.py:20-27:

python
viewer_id = None

# Parse optional --viewer_id
if len(sys.argv) > 3 and sys.argv[2] == "--viewer_id":
    viewer_id = sys.argv[3]
    
db = DBManager()
results = db.search_person(query, viewer_id=viewer_id)

SKILL.md:18-25:

markdown
- **User Privacy**: For private entries (e.g., family members), ensure you pass the correct `viewer_id` (e.g., `1234567890` for the specific authorized user).

## Workflows

### 1. Searching for a Person
When a user asks about a specific historical figure or family member:
- Run the python search script passing the name and the user's ID for privacy.
- Example: `python3 scripts/search_person.py "康熙" --viewer_id "5104087055"`

Technical Analysis

The script accepts viewer_id directly from a command-line argument and passes it to the database privacy layer. It performs no authentication, signature verification, session binding, or other check proving that the caller owns the supplied identity.

The documentation indicates that viewer_id is used to access private family entries and provides a concrete ID-shaped value in an example. If DBManager.search_person treats equality between this supplied value and a record's creator_id as sufficient authorization, knowledge or guessing of another user's ID is enough to impersonate that user.

Authorization identifiers must be derived from a trusted, authenticated execution context. A caller-supplied identifier is a claim, not proof of identity.

Attack Path

  1. An attacker learns or guesses the Telegram ID associated with a private record. Such identifiers may be disclosed through logs, examples, database results, or other interactions.
  2. The attacker invokes ...[truncated 1057 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove unrestricted --viewer_id input from security-sensitive production workflows.
  2. Derive the viewer identity from an authenticated Telegram update, signed token, or trusted service session.
  3. Pass an authenticated principal object to the database layer rather than a bare user-controlled string.
  4. Enforce authorization again inside DBManager.search_person, including deny-by-default handling when no verified identity is present.
  5. If command-line operation is necessary, require a short-lived signed credential and verify its issuer, audience, expiration, and binding to the requested viewer ID.
  6. Avoid displaying real or production-like user identifiers in documentation.
  7. Add tests confirming that a caller cannot obtain another creator's private records by supplying that creator's ID.
  8. Log denied access attempts without recording sensitive query results or authentication credentials.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/resolve_relation.py:31
Finding

Relationship Resolution Traverses Person Records Without Viewer Authorization Context

Content
View full analysis

Vulnerability Details

File Location: scripts/resolve_relation.py:31-39 and scripts/resolve_relation.py:56-69
Vulnerability Type: Missing authorization context in private-data lookup
Risk Level: High

Vulnerable Code

scripts/resolve_relation.py:31-39:

python
engine = RelationshipEngine()

# Quick hack: If target_relation is a name, _get_person will find it
# If it's a role like "爷爷", we will search DB ourselves

p_b = engine._get_person(target_relation)
if p_b:
    # It's a name vs name check
    result = engine.check_relationship_two_people(start_person, target_relation)

scripts/resolve_relation.py:56-69:

python
if "爷" in target_relation or "祖父" in target_relation:
    # Grandparent logic: A -> parents -> parents
    parents = rj.get("parents", [])
    grandparents = []
    for p_str in parents:
        p_name = p_str.split("(")[0].strip()
        p_res = db.search_person(p_name)
        if p_res:
            p_p = p_res[0]
            p_rj = p_p.get("relations_json")
            if isinstance(p_rj, str):
                try:
                    p_rj = json.loads(p_rj)

The initial lookup at scripts/resolve_relation.py:39 is also performed without viewer context:

python
res = db.search_person(start_person)

Technical Analysis

The project documentation states that the database contains both public historical figures and private family members and that private searches require a correct viewer_id. The relationship script, however, provides no viewer identity parameter and does not propagate an authenticated principal through its lookup operations.

It calls the private method engine._get_person, invokes check_relationship_two_people, and performs direct and recursive db.search_person calls without an authorization context. This creates an alternate data-access path that does not implement the privacy workflow documented ...[truncated 1664 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require an authenticated viewer context for every relationship request.
  2. Propagate the verified principal through _get_person, check_relationship_two_people, and every direct or recursive search_person call.
  3. Do not call private methods such as _get_person as an authorization-sensitive shortcut. Expose a supported API that requires an authenticated principal.
  4. Enforce record-level authorization inside DBManager and RelationshipEngine, not only in CLI wrappers.
  5. Apply authorization at every graph-traversal step. A user permitted to view one person must not automatically receive access to all connected private people.
  6. Return a uniform not-found or unauthorized response to prevent private-record existence probing.
  7. Add tests for anonymous queries, cross-owner queries, mixed public/private relationship paths, and recursive traversal into unauthorized records.
  8. Ensure errors and relationship output do not reveal private names or metadata when authorization fails.

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/search_person.py:5
Finding

Unpinned Imports Allow Hijacking of Security-Critical External Modules

Content
View full analysis

Vulnerability Details

File Location: scripts/search_person.py:5-11 and scripts/resolve_relation.py:5-14
Vulnerability Type: Python module search-path hijacking
Risk Level: High

Vulnerable Code

scripts/search_person.py:5-11:

python
bot_dir = "/home/admin/.openclaw/workspace/liudao-bot"
sys.path.append(bot_dir)
os.chdir("/home/admin/.openclaw/workspace")

try:
    from db_manager import DBManager
except ImportError:

scripts/resolve_relation.py:5-14:

python
# Add bot directory to path to import relation_engine
bot_dir = "/home/admin/.openclaw/workspace/liudao-bot"
sys.path.append(bot_dir)
# IMPORTANT: do NOT os.chdir(bot_dir) because db_manager uses relative path to data/liudao.db 
# based on __file__ of db_manager.py, but actually it might be hardcoded to liudao-bot/data/liudao.db
# Let's check db_manager.py path handling:
os.chdir("/home/admin/.openclaw/workspace")

try:
    from relation_engine import RelationshipEngine

Technical Analysis

Both scripts execute security-critical code imported from an external, mutable workspace that is not included in the audited package. The modules are imported by generic top-level names, db_manager and relation_engine, rather than through a uniquely named and integrity-controlled package.

sys.path.append(bot_dir) places the intended directory at the end of the existing module search path. Consequently, a same-named module in an earlier search location can be selected first. Python executes top-level module code during import, so successful path hijacking results in immediate code execution before normal database processing begins.

Changing the current working directory does not securely bind imports to the intended files. It also increases reliance on ambient filesystem state. Independently, an attacker able to modify the external liudao-bot modules can replace the implementation because no version pinning, si ...[truncated 1515 chars]

Remediation
View remediation

Remediation Suggestions

  1. Package the required database and relationship modules inside the audited skill under a unique Python package namespace.
  2. Use explicit package imports, such as from liudao_heritage.db_manager import DBManager, rather than generic top-level module names.
  3. Remove runtime sys.path manipulation and dependence on mutable absolute workspace paths.
  4. Install dependencies into a dedicated, read-only virtual environment owned by a trusted administrator.
  5. Pin dependency versions and verify package hashes or signed release artifacts during deployment.
  6. Ensure code directories are not writable by the service account at runtime or by lower-trust users and processes.
  7. If an external module is unavoidable, resolve its canonical path, validate ownership and permissions, verify a trusted cryptographic digest, and load it using a controlled mechanism.
  8. Run the skill under a least-privileged account with restricted filesystem and network access so that an import compromise has limited impact.
  9. Include all security-critical imported code in future audit scope.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (4)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill directs use of a viewer_id and even includes a concrete example identifier for privacy-gated lookups, but gives no guidance on minimizing exposure, avoiding leakage in logs, or verifying that the identifier belongs to the current authorized user. This creates risk of improper disclosure of personal data through identifier misuse, hardcoded IDs, or transmission/storage of personal identifiers without safeguards.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill explicitly instructs the agent to insert or edit personal records in a live SQLite database but does not require user confirmation, disclose that persistent state will be modified, or constrain when writes are allowed. In an agent setting, this can lead to unauthorized or accidental data tampering, privacy violations, and integrity loss if a prompt causes the model to perform writes without sufficiently explicit consent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The schema explicitly documents storage of private family-member records, sensitive personal attributes, and Telegram creator IDs, but provides no documented retention limits, access-control guarantees, consent requirements, or user-facing privacy warning. In a genealogy skill, this increases the risk of collecting and exposing personal data about living individuals beyond user expectations, especially because ownership is tied to a platform identifier.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
75% confidence
Finding

The schema documentation presents field values and examples exclusively in Chinese, including enumerated values such as gender and calendar type, without indicating that other languages or locales are supported. This can be a natural-language locale constraint if the skill expects or enforces Chinese-only input without user opt-in or documented justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.