Back to skill

Security audit

hr-recruiting-tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill’s HR workflow is mostly coherent, but it handles candidate PII and Tencent Docs access with several under-scoped defaults that users should review before installing.

Install only if you control the Tencent Docs environment and can pin or verify mcporter and tencent-docs. Prefer passing an explicit --file-id, avoid ambiguous same-name sheets, review the external auth script path before running it, and treat generated resume bundles and uploaded records as confidential candidate PII.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T08 · Insecure Dependencies

Error
Location
references/workflow_candidate_upload.md:30
Finding

Unpinned and Registry-Ambiguous Executable Dependencies

Content
View full analysis

Vulnerability Details

File Location: references/workflow_candidate_upload.md:30-51
Vulnerability Type: Supply-chain dependency substitution
Risk Level: High

Vulnerable Code

markdown
### 1. mcporter CLI

```bash
npm install -g mcporter

Verification:

bash
mcporter --version

2. Tencent Docs Authorization

First ensure that the tencent-docs Skill is installed. Prefer the Skill registry supported by the current runtime, for example:

bash
openclaw skills install tencent-docs
openclaw skills info tencent-docs

If SkillHub, ClawHub, or another registry is used, a Skill with the same name may also be installed.

text

Equivalent unpinned installation guidance also appears in:

- `references/dependency_contracts.md:109-123`
- `SKILL.md:123-133`

### Technical Analysis

The workflow instructs users to globally install `mcporter` and install a same-named `tencent-docs` Skill without specifying an exact version, package digest, trusted publisher, or mandatory registry. It also explicitly permits alternative registries to provide a Skill under the same name.

Both dependencies participate directly in sensitive operations. `mcporter` receives serialized candidate records and invokes the remote MCP service, while the `tencent-docs` Skill participates in authorization and token retrieval. A substituted or compromised dependency could therefore inspect candidate PII, access authentication material available to the process, or execute arbitrary code with the invoking user's privileges.

Global npm installation increases the potential impact because package installation lifecycle scripts execute in the user's environment. Merely checking `mcporter --version` does not authenticate the package or establish its integrity.

### Attack Path

1. An attacker publishes a malicious package or Skill using the expected name, or compromises an accepted registry or package version.

...[truncated 910 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin mcporter to a reviewed exact version instead of installing the latest package:
    bash
    npm install --global mcporter@<reviewed-version>
    
  2. Record and verify package integrity using a lockfile, registry-supported integrity metadata, signed provenance, or an independently published checksum.
  3. Require a specific trusted registry and verified publisher. Remove guidance allowing arbitrary registries to supply a same-named Skill.
  4. Pin the tencent-docs Skill to a reviewed immutable release or commit and verify its signature or digest before use.
  5. Prefer an isolated virtual environment, container, or project-local dependency installation over a global npm installation.
  6. Disable or strictly control package lifecycle scripts where operationally possible.
  7. Document the expected package identity, publisher, version, registry URL, and cryptographic digest.
  8. Re-review dependencies before upgrades and test them in a sandbox without production credentials or candidate data.

T08 · Insecure Dependencies

Error
Location
references/workflow_candidate_upload.md:53
Finding

Arbitrary External Script Execution Through Environment-Controlled Skill Directory

Content
View full analysis

Vulnerability Details

File Location: references/workflow_candidate_upload.md:53-66
Vulnerability Type: Unverified external code execution in an authentication workflow
Risk Level: High

Vulnerable Code

bash
# Check authorization status
export TENCENT_DOCS_SKILL_DIR="${TENCENT_DOCS_SKILL_DIR:-$HOME/.openclaw/workspace/skills/tencent-docs}"
bash "$TENCENT_DOCS_SKILL_DIR/setup.sh" tdoc_check_and_start_auth

# If AUTH_REQUIRED is returned, let the user complete authorization in a browser
# Fetch the token after authorization
bash "$TENCENT_DOCS_SKILL_DIR/setup.sh" tdoc_fetch_token

# Manually configure the token as an alternative
mcporter config add tencent-docs "https://docs.qq.com/openapi/mcp" \
    --header "Authorization=$TENCENT_DOCS_TOKEN" \
    --transport http --scope home

Equivalent external-script execution guidance also appears in:

  • references/dependency_contracts.md:132-141
  • SKILL.md:217-221
  • references/workflow_job_management.md:68-72

Technical Analysis

The authentication instructions execute setup.sh from a directory controlled by the TENCENT_DOCS_SKILL_DIR environment variable. Quoting the path prevents shell word splitting but does not establish that the selected file is trustworthy.

No implementation in this project verifies the resolved path, file ownership, permissions, symlink status, pinned version, signature, or digest of setup.sh. The documentation says that the external Skill should be reviewed, but this is a procedural statement rather than an enforceable control.

The tdoc_fetch_token operation is consistent with the declared Tencent Docs functionality and is not, by itself, evidence of credential theft. The vulnerability is that an unverified external script is allowed to execute inside a token-handling workflow. If the environment variable or dependency installation is poisoned, the selected script can execute arbitrary commands and acce ...[truncated 1184 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not execute an authentication script from an unrestricted environment-selected directory.
  2. Resolve the dependency only from an allowlisted installation root and canonicalize the path with realpath.
  3. Reject paths outside the trusted root, symbolic links, files owned by unexpected users, and files or parent directories writable by untrusted users.
  4. Pin the external Skill to an immutable reviewed release and verify a cryptographic signature or digest immediately before execution.
  5. Replace shell-script delegation with a narrow, versioned authentication API or a trusted executable that exposes only the required operations.
  6. Run authorization tooling in a sandbox with minimal filesystem access and a restricted environment.
  7. Pass only required environment variables rather than inheriting the complete parent environment.
  8. Never print token values, command lines containing tokens, or unredacted authentication responses.
  9. Require explicit user confirmation before token retrieval and clearly identify the verified script path and version.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/upload_to_smartsheet.py:151
Finding

Candidate PII Can Be Uploaded to an Ambiguous Same-Named Document

Content
View full analysis

Vulnerability Details

File Location: scripts/upload_to_smartsheet.py:151-169
Vulnerability Type: Unsafe remote destination selection
Risk Level: High

Vulnerable Code

python
def resolve_smartsheet_target(
    file_id: str | None,
    sheet_title: str,
    space_id: str | None,
    create_new: bool,
    dry_run: bool = False,
    probe_remote: bool = False,
) -> tuple[str | None, bool, str]:
    """Resolve the candidate repository target. Reuse the fixed title by default and create it only if absent."""
    if file_id:
        return file_id, False, "use_explicit_file_id"

    if dry_run and not probe_remote:
        return None, False, "dry_run_offline"

    if not create_new:
        matches = find_smartsheets_by_title(sheet_title)
        if matches:
            if len(matches) > 1:
                print(f"  Notice: found {len(matches)} candidate repositories with the same name; the first search result will be used. Pass --file-id to specify one.")
            return matches[0]["file_id"], False, "reuse_existing_title"

    if dry_run:
        return None, True, "would_create"

    return create_smartsheet(sheet_title, space_id), True, "created_new"

The selected destination is later used for the candidate record upload at scripts/upload_to_smartsheet.py:629-631:

python
# 7. Upload record
print("📤 Uploading candidate record...")
result = add_records(file_id, sheet_id, [record])

Technical Analysis

If more than one SmartSheet has the fixed title HR候选人库, the program warns the operator but automatically selects the first search result. It does not verify the document owner, workspace, knowledge-base space, access-control list, or any previously approved destination identifier.

Search-result ordering is not a security boundary and may be unstable or attacker-influenced. Because the uploaded record includes candidate names, telephone numbers, em ...[truncated 1427 chars]

Remediation
View remediation

Remediation Suggestions

  1. Fail closed when more than one exact-title match is returned.
  2. Require an explicit --file-id whenever the destination cannot be uniquely established.
  3. Retrieve and display destination owner, workspace, space ID, and access metadata before uploading sensitive data.
  4. Require explicit operator confirmation of the resolved destination for PII uploads.
  5. Maintain an allowlist of approved candidate-repository file IDs in protected local configuration.
  6. When --space-id is supplied, ensure searches and destination selection are restricted to that space.
  7. Add a preflight operation that verifies the expected schema and a repository-specific marker before transmitting records.
  8. Log the approved destination identifier without logging candidate PII.
  9. Add tests proving that multiple matches abort the operation and result in no call to smartsheet.add_records.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_to_smartsheet.py:469
Finding

Absolute Local Resume-Bundle Path Is Disclosed to Tencent Docs

Content
View full analysis

Vulnerability Details

File Location: scripts/upload_to_smartsheet.py:469-516
Vulnerability Type: Unnecessary disclosure of local filesystem metadata
Risk Level: Medium

Vulnerable Code

python
# HR review flag
review_required = report.get("review_required", quality != "high")

# Resume bundle path
bundle_uri = f"file://{bundle_dir}"

now_ms = str(int(time.time() * 1000))

field_values = []

def add_text(field: str, value: str | None):
    if value:
        field_values.append({
            "field": field,
            "text_value": {"items": [{"text": str(value), "type": "text"}]}
        })

def add_number(field: str, value: float | None):
    if value is not None:
        field_values.append({"field": field, "number_value": value})

def add_bool(field: str, value: bool):
    field_values.append({"field": field, "bool_value": value})

def add_string(field: str, value: str | None):
    if value:
        field_values.append({"field": field, "string_value": str(value)})

# Map all fields
add_text("姓名", name)
add_string("电话", phone)
add_string("邮箱", email)
add_text("当前公司", company)
add_number("工作年限", years)
add_text("最高学历", degree)
add_text("毕业院校", school)
add_text("专业", major)
add_number("毕业年份", int(grad_year) if grad_year and grad_year.isdigit() else None)
add_text("技能标签", skill_text)
add_text("求职意向", None)
add_text("招聘阶段", "简历筛选")
add_text("简历来源", resume_file)
add_text("解析质量", quality_label)
add_bool("需HR审核", review_required)
add_text("简历包路径", bundle_uri)

The schema explicitly defines this remotely stored field at assets/schemas/recruiting_tables.json:29:

json
{"key": "bundle_path", "field_title": "简历包路径", "field_type": "text", "description": "本地简历包目录路径", "property_text": {}}

Technical Analysis

bundle_dir is derived from:

python
bundle_dir = Path(args.bundle_dir).expanduser().resolve()

It is therefore an ...[truncated 1810 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove the local bundle-path field from records sent to Tencent Docs.
  2. Replace it with an opaque random bundle identifier or a non-reversible reference generated specifically for remote tracking.
  3. Keep the mapping from the opaque identifier to the absolute local path in a local file or database with restrictive permissions.
  4. If correlation is required, use a keyed HMAC rather than exposing the path or a raw hash of predictable path values.
  5. Update assets/schemas/recruiting_tables.json, workflow documentation, and existing migration logic to remove bundle_path.
  6. Ensure terminal redaction and remote payload minimization are treated as separate controls.
  7. Add an automated test that rejects file://, home-directory paths, and absolute filesystem paths in outbound SmartSheet records.
  8. Review existing remote records and delete previously uploaded local paths where operationally possible.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to run local Python scripts, read and write resume bundles, access environment variables such as TENCENT_DOCS_TOKEN, and invoke shell commands, but it does not declare any explicit tool scope or allowed-tools boundaries. In an agent environment, this weakens least-privilege controls and can allow broader file, shell, or secret access than users and reviewers expect, which is especially sensitive here because the workflow handles confidential resume data and optional Tencent Docs credentials.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This JSON schema uses fixed Chinese sheet titles and descriptions such as "HR候选人库" and Chinese-only field metadata, which constitutes a language/locale constraint in natural-language content. The file does not indicate that Chinese is optional, user-selected, or justified as a region-specific requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This markdown prompt is written entirely in Chinese and prescribes the extraction workflow in that locale, but it does not indicate that language selection is optional or limited to a China-specific deployment. Under the policy rule for natural-language violations, forcing a specific language without user opt-in can be a locale-policy issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The entire dependency contract is written in Chinese and includes fixed Chinese field/table names such as HR候选人库, without any indication that users may choose another language or that the skill is restricted to a Chinese-only regional context. Under the policy, forcing a specific language without user opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file title and all operational instructions are written in Chinese, and there is no note that the skill supports other languages or that Chinese is required due to a region-specific compliance or business constraint. 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.

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

python
if not command:
        return False, f"未找到命令:{command_name}"
    try:
        proc = subprocess.run(
            [command, *args],
            text=True,
            capture_output=True,

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

python
tencent_docs_detail = "未远程探测;使用 --probe-remote 验证腾讯文档 MCP 和授权。"
    if mcporter_ready and probe_remote:
        try:
            proc = subprocess.run(
                ["mcporter", "call", "tencent-docs", "smartsheet.list_tables",
                 "--args", '{"file_id":"_check_"}'],
                text=True, capture_output=True, timeout=15, check=False,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file's docstring and all CLI/help/status messages are in Chinese, which imposes a specific language on users without any opt-in or alternative. Under the language/locale policy, this is a natural-language policy issue unless the locale constraint is explicitly documented and justified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The dry-run path serializes and prints the full imported job records to stdout, which can expose sensitive recruiting data such as salary ranges, hiring manager names, interview process details, and internal role requirements to terminal history, CI logs, or centralized logging systems. In this HR/recruiting context, that data is business-sensitive and may be broadly accessible once logged, so the leak surface is real even though no network exfiltration is performed by the script itself.

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

python
str(path),
        ]

        md_proc = subprocess.run(
            [common_args[0], "--to", "md", *common_args[1:]],
            text=True,
            capture_output=True,

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

python
markdown = markdown_file.read_text(encoding="utf-8") if markdown_file else md_proc.stdout

        raw_doc: dict[str, Any] = {}
        json_proc = subprocess.run(
            [common_args[0], "--to", "json", *common_args[1:]],
            text=True,
            capture_output=True,

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

python
if not pdftotext:
        raise ExtractionError("pdftotext CLI 不可用")

    proc = subprocess.run(
        [pdftotext, "-layout", str(path), "-"],
        text=True,
        capture_output=True,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script automatically writes the original resume, extracted markdown, parsed JSON, and candidate draft containing personal data to disk, and even creates the output directory before extraction succeeds. In an HR context this is sensitive PII, so silent persistence increases the chance of unauthorized local access, accidental retention, backup leakage, or mishandling on shared systems.

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

python
def mcporter_call(tool: str, args: dict) -> dict:
    """调用 tencent-docs MCP 工具。"""
    payload = json.dumps(args, ensure_ascii=False)
    proc = subprocess.run(
        ["mcporter", "call", "tencent-docs", tool, "--args", payload],
        text=True, capture_output=True, timeout=60, check=False,
    )

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest description presents core usage guidance partly in Chinese and partly in English, but does not state a user-selectable language preference or opt-in. This can violate language/locale policy expectations when a skill effectively assumes a language for its instructions or activation context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

All user-facing instructions in the file are presented in Chinese, with no indication that another language is available or that Chinese is a documented requirement. This can violate a language-choice policy when users are not given an explicit locale option or justification for the restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The workflow explicitly instructs creating, reusing, and modifying a fixed Tencent Docs spreadsheet and importing job records, but it does not require an explicit user confirmation at the point of external write. In an agent setting, this can cause unintended writes to a real HR document or space, especially because the target table name is fixed and reuse is the default behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

该技能说明全文以中文编写,并直接规定“成功提取后,回复以下内容”,但未说明这是面向中文用户的区域性/组织性要求,也未给用户提供语言偏好选择。根据语言/locale 政策,这会构成未获用户选择的语言约束。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file’s user-facing description is written only in Chinese ("检查 hr-recruiting-tracker 工作流所需的能力。"), and the script’s CLI help and status messages are likewise hard-coded in Chinese. This creates a locale/language constraint without offering the user a language choice or documenting that the tool is intentionally region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

Natural-language strings in this file, including the module docstring and CLI help text, are Chinese-only, which effectively forces a specific language for user interaction. The file does not offer an alternative language or explain that the tool is intentionally region- or locale-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The raised KeyError message is hard-coded in Chinese ("未知招聘表模型") and appears to be user-visible when an invalid table name is supplied. Under the stated policy, forcing a specific language without user choice or documented justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

This Python skill’s docstring, argument help text, runtime prompts, and status/error messages are all written in Chinese, which imposes a specific language on users. Under the stated policy, locale or language constraints should either be optional or explicitly justified; this file does not provide a user language choice or a documented region-specific justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.