Back to skill

Security audit

citic--auto--skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for a credit-card recommendation and prefill assistant, but it handles highly sensitive applicant data with under-enforced consent and stores raw values in plaintext outputs.

Review before installing. Use this only in a private workspace, avoid putting real ID numbers or financial details in files that may be logged or committed, and do not run the extractor or plan builder unless you intentionally want workspace memory scanned. Generated output files should be treated as sensitive personal financial data.

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

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/profile_extractor.py:87
Finding

Workspace Memory Is Scanned Without Programmatic Consent Enforcement

Content
View full analysis

Vulnerability Details

File Location: scripts/profile_extractor.py:87-100, 110-145; scripts/build_application_plan.py:49-55
Vulnerability Type: Missing authorization enforcement for sensitive workspace data
Risk Level: Medium

Vulnerable Code

python
def collect_text_sources(workspace: Path) -> List[tuple[str, str]]:
    files: List[tuple[str, str]] = []
    for rel in ('USER.md', 'MEMORY.md'):
        p = workspace / rel
        if p.exists():
            files.append((rel, scan_text_file(p)))
    mem_dir = workspace / 'memory'
    if mem_dir.exists():
        for p in sorted(mem_dir.glob('*.md')):
            files.append((str(p.relative_to(workspace)), scan_text_file(p)))
    return files
python
def build_candidates(workspace: Path) -> List[Candidate]:
    candidates: List[Candidate] = []
    json_profile = load_json_profile(workspace)

    # Structured JSON is preferred and can include high-sensitivity fields explicitly provided by the user.
    for field, value in json_profile.items():
        if value in ('', None, [], {}):
            continue
        if field.startswith('consent_') or field == 'notes':
            continue
        if isinstance(value, (list, dict, bool, int, float)):
            value_str = json.dumps(value, ensure_ascii=False)
        else:
            value_str = str(value)
        candidates.append(Candidate(
            field=field,
            value=value_str,
            source='profiles/applicant_profile.json or template',
            confidence='high',
            masked_preview=mask_value(field, value_str),
        ))

    existing_fields = {c.field for c in candidates}
    for source_name, text in collect_text_sources(workspace):
        for field in FIELD_PATTERNS:
            if field in existing_fields:
                continue
            extracted = extract_from_text(field, text)
            if extract
...[truncated 3171 chars]
Remediation
View remediation

Remediation Suggestions

  1. Make memory access disabled by default and require an explicit option such as --include-memory.
  2. Require a separate, auditable consent value rather than inferring consent from invocation alone.
  3. Pass authorization into build_candidates(), for example:
    python
    def build_candidates(
        workspace: Path,
        include_memory: bool = False,
        approved_sources: Optional[List[Path]] = None,
    ) -> List[Candidate]:
    
  4. Do not call collect_text_sources() unless include_memory is explicitly true.
  5. Require the caller to identify individually approved files instead of scanning all memory/*.md.
  6. Make build_application_plan.py fail closed unless the required consent option is supplied.
  7. Keep browser-prefill consent separate from permission to read memory; authorization for one action must not imply authorization for the other.
  8. Record which sources were authorized and display that list before extraction begins.
  9. Add tests proving that the default invocation never opens USER.md, MEMORY.md, or memory/*.md.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/profile_extractor.py:158
Finding

Raw Applicant Data Is Printed and Stored in Predictable Plaintext JSON Files

Content
View full analysis

Vulnerability Details

File Location: scripts/profile_extractor.py:158-173; scripts/build_application_plan.py:55-98
Vulnerability Type: Plaintext exposure and insecure persistence of sensitive personal data
Risk Level: High

Vulnerable Code

python
workspace = Path(args.workspace).resolve()
candidates = build_candidates(workspace)
payload = {
    'workspace': str(workspace),
    'warning': 'Candidates only. Confirm every field with the user before filling the official application form. Do not auto-submit.',
    'candidates': [asdict(c) for c in candidates],
}

if args.out:
    out_path = Path(args.out)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding='utf-8')

if args.json:
    print(json.dumps(payload, ensure_ascii=False, indent=2))
    return
python
candidates = [c.__dict__ for c in build_candidates(workspace)]

top_cards: List[Dict[str, Any]] = recommendation.get('top_cards', [])
recommended = top_cards[0] if top_cards else {}

plan = {
    'workflow_name': workflow.get('name'),
    'workspace': str(workspace),
    'browser_profile_default': (workflow.get('profiles') or {}).get('browser_profile_default', 'openclaw'),
    'browser_profile_existing_session': (workflow.get('profiles') or {}).get('browser_profile_existing_session', 'user'),
    'recommended_card': recommended,
    'backup_cards': top_cards[1:3],
    'candidate_fields': candidates,
    'confirmed_fields_required': [
        'full_name', 'phone', 'id_type', 'city', 'employer_name', 'monthly_income'
    ],
    'safe_prefill_fields': [
        'full_name', 'gender', 'birth_date', 'phone', 'email', 'city', 'residential_address',
        'mailing_address', 'employer_name', 'employer_city', 'job_title', 'education', 'monthly_income',
        'housing_status'
    ],
    'never_autofill_without_user_live
...[truncated 3629 chars]
Remediation
View remediation

Remediation Suggestions

  1. Do not serialize raw candidate values into the application plan. Store only field names, masked previews, source identifiers, and confirmation state.
  2. Keep raw values in memory only for the shortest period required to perform a confirmed browser action.
  3. Make masked output the default and require explicit, narrowly scoped authorization before emitting any raw value.
  4. Avoid printing raw values to standard output because stdout is commonly logged.
  5. If persistence is strictly necessary, obtain separate user consent and create files atomically with owner-only permissions:
    python
    import os
    
    fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    with os.fdopen(fd, "w", encoding="utf-8") as handle:
        json.dump(masked_payload, handle, ensure_ascii=False, indent=2)
    
  6. Reject output paths outside an approved private directory and prevent writes through symbolic links.
  7. Add generated profile and plan files to ignore rules and provide warnings against committing them.
  8. Implement a defined retention period and secure cleanup after browser handoff.
  9. Separate recommendation preferences from high-sensitivity application fields so the recommendation plan never needs identity data.
  10. Add automated tests confirming that serialized payloads and stdout never contain raw phone numbers, ID numbers, addresses, or other protected values.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

PyYAML Dependency Is Not Pinned to a Reviewed Version

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-2; README.md:78-82
Vulnerability Type: Unbounded third-party dependency resolution
Risk Level: Low

Vulnerable Code

text
# No third-party dependencies required for the included helper scripts.
PyYAML>=6.0

The documented installation command is:

bash
pip install -r requirements.txt

Technical Analysis

The lower-bound-only requirement allows pip to install any available PyYAML release at or above version 6.0. This makes installations non-reproducible and permits future versions that were not reviewed with this Skill to enter the execution environment.

No evidence was found that PyYAML is currently malicious, typosquatted, or retrieved from an unsafe custom source. The risk is therefore prospective supply-chain exposure rather than evidence of an active malicious dependency.

The application uses yaml.safe_load(), which is appropriate for parsing untrusted YAML and avoids the unsafe object-construction behavior of yaml.load(). The finding is limited to dependency resolution and integrity controls.

Attack Path

  1. A user follows the README and runs pip install -r requirements.txt.
  2. Pip queries its configured package index and resolves the newest compatible PyYAML release rather than a reviewed, fixed artifact.
  3. A future compromised, vulnerable, or behaviorally incompatible release satisfies PyYAML>=6.0.
  4. Pip downloads and installs that release, potentially executing package build or installation logic.
  5. The unreviewed dependency then executes in the same Python environment as the plan builder.

Successful exploitation depends on compromise or vulnerability of a future compatible package release, a malicious configured package index, or another supply-chain failure. The audited repository itself does not provide such a payload.

Impact Assessment

Installation-time code would run with th ...[truncated 386 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin PyYAML to a specific reviewed version instead of using a lower-bound-only constraint.
  2. Generate and verify cryptographic hashes, for example through a locked requirements file used with pip install --require-hashes.
  3. Separate human-authored dependency input from a fully resolved deployment lock file.
  4. Use a trusted package index explicitly configured through deployment policy.
  5. Review and update the pinned version on a controlled schedule after vulnerability and compatibility testing.
  6. Run installation in an isolated virtual environment under a non-privileged account.
  7. Consider removing PyYAML entirely if the small static workflow configuration can be represented in JSON or parsed without an additional dependency.
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

声明强调这是一个会借助浏览器工具在官网申请页上进行辅助预填、并在最终提交前停下等待用户确认的申请助手。但实际代码仅是一个命令行脚本:本地读取 card_catalog.json,基于客户画像做规则打分推荐,并输出推荐 Markdown/JSON 或单卡申请步骤说明。assist() 也只是输出申请页链接和材料清单,没有任何网页交互、自动化填表、网络访问、提交控制或用户确认节点。因此其核心已实现能力只是“卡种推荐 + 文本化申请指引”,而不是声明中的“浏览器辅助官网预填申请”。这属于对实际能力的重要夸大,应判定为描述与行为不符。

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- `SKILL.md`:告诉 agent 何时调用哪些工具。

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill can read and write local files via its documented scripts, but it does not declare any explicit tool scope or permissions boundary. In this context, the skill is designed to access highly sensitive applicant data from workspace files, so missing scope declarations increases the risk of over-broad data access or accidental exposure if the agent invokes file capabilities too broadly.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The entire safety note is written only in Chinese, with no indication that users may choose another language or that the locale restriction is intentional. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The natural-language value on L07 fixes the identity document to '中华人民共和国居民身份证', which imposes a specific national/locale assumption in the template. Under the language/locale policy, this is a policy concern because users are not given an explicit choice or opt-in for alternative locales or document types.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This JSON file contains user-facing natural-language fields such as names, highlights, tags, and notes exclusively in Chinese. If this skill is intended for a broader audience, forcing a single language without documented opt-in or locale justification can violate language/locale policy requirements.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency is specified as PyYAML>=6.0 without an upper bound or exact pin, which makes builds non-reproducible and allows future installs to resolve to unexpected versions. While this file alone does not prove exploitation, unpinned dependencies increase supply-chain risk and make it harder to ensure that only reviewed versions are deployed.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
# No third-party dependencies required for the included helper scripts.
PyYAML>=6.0

Unverifiable Dependency: PyYAML has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

PyYAML has multiple historical security advisories, and because the manifest does not pin a specific version, it is impossible to verify from this file whether a safe release will be installed. In a skill that may process user-provided or remote configuration data, uncertainty around the installed parser version increases the risk of unsafe parsing behavior or exposure to known issues.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The error message at L020 is hard-coded in Chinese ("缺少 PyYAML,请先安装 requirements.txt") with no indication that the skill supports multiple languages or that Chinese is a required locale. This can violate language/locale policy when the skill is not clearly documented as region-specific or offering user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code emits user-facing text in Chinese, including error messages and recommendation/application guidance, with no option to select another language. That can violate a language/locale policy when tools are expected to respect user preference or provide opt-in for a fixed locale.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.