Back to skill

Security audit

HiHired Resume

Security checks for vulnerabilities and agentic risk

Overview

The skill fits its resume-help purpose, but it tells agents to send sensitive resume data to a raw plaintext HTTP backend without a clear consent step.

Review before installing. Use the chat drafting and HiHired web handoff only if acceptable, and avoid the API helper unless the user explicitly agrees to send resume, job-description, and cover-letter data to HiHired and the endpoint is changed to a verified HTTPS host.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hihired_api.py:10
Finding

Sensitive Resume Data Is Transmitted over Plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/hihired_api.py:10, with the insecure transport used by the request functions at scripts/hihired_api.py:20-27 and scripts/hihired_api.py:30-61
Vulnerability Type: Plaintext transmission of sensitive information
Risk Level: High

Vulnerable Code

python
DEFAULT_BASE = os.environ.get("HIHIRED_BASE_URL", "http://18.190.155.165")
python
def post_json(base_url: str, endpoint: str, payload: dict):
    body = json.dumps(payload).encode("utf-8")
    req = request.Request(
        f"{base_url.rstrip('/')}{endpoint}",
        data=body,
        headers={"Content-Type": "application/json", "Accept": "application/json"},
        method="POST",
    )
    with request.urlopen(req, timeout=DEFAULT_TIMEOUT) as resp:
        return parse_json_response(resp)
python
def post_multipart(base_url: str, endpoint: str, file_path: str, field_name: str = "resume"):
    path = Path(file_path)
    if not path.exists():
        raise FileNotFoundError(file_path)

    boundary = "----OpenClawHiHiredBoundary7MA4YWxkTrZu0gW"
    content_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
    file_bytes = path.read_bytes()

    parts = []
    parts.append(f"--{boundary}\r\n".encode())
    parts.append(
        f'Content-Disposition: form-data; name="{field_name}"; filename="{path.name}"\r\n'.encode()
    )
    parts.append(f"Content-Type: {content_type}\r\n\r\n".encode())
    parts.append(file_bytes)
    parts.append(f"\r\n--{boundary}--\r\n".encode())
    body = b"".join(parts)

    req = request.Request(
        f"{base_url.rstrip('/')}{endpoint}",
        data=body,
        headers={
            "Content-Type": f"multipart/form-data; boundary={boundary}",
            "Accept": "application/json",
        },
        method="POST",
    )
    with request.urlopen(req, timeout=DEFAULT_TIMEOUT) as re
...[truncated 2596 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the plaintext default with an authenticated HTTPS hostname whose certificate is valid for that hostname.
  2. Reject all base URLs that do not use https://; do not provide an HTTP fallback.
  3. Maintain an explicit allowlist of approved API hostnames and reject raw IP addresses and arbitrary destinations by default.
  4. Avoid allowing untrusted environment variables or command-line values to silently redirect sensitive uploads. If custom endpoints are necessary, require an explicit high-friction opt-in and display the destination before transmission.
  5. Obtain informed user consent before uploading a resume, clearly identifying the recipient, the fields transmitted, and applicable retention behavior.
  6. Minimize transmitted information to only the fields required for the selected operation.
  7. Ensure normal TLS certificate and hostname verification remains enabled, and fail closed on certificate errors.
  8. Update SKILL.md and references/hihired-capabilities.md to remove guidance recommending the direct plaintext backend.
  9. Consider adding request-size limits, file-type validation, and privacy-preserving logging controls as defense-in-depth measures.
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tainted flow: 'DEFAULT_TIMEOUT' from os.environ.get (line 12, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/hihired_api.py (reported line 29)May include surrounding context.

python
headers={"Content-Type": "application/json", "Accept": "application/json"},
        method="POST",
    )
    with request.urlopen(req, timeout=DEFAULT_TIMEOUT) as resp:
        return parse_json_response(resp)

Tainted flow: 'DEFAULT_TIMEOUT' from os.environ.get (line 12, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/hihired_api.py (reported line 61)May include surrounding context.

python
headers={"Content-Type": "application/json", "Accept": "application/json"},
        method="POST",
    )
    with request.urlopen(req, timeout=DEFAULT_TIMEOUT) as resp:
        return parse_json_response(resp)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The file instructs agent-side API calls to use a direct backend IP address instead of the normal public domain, specifically to bypass Cloudflare protections that may block scripted access. Sending user resume or job data to a raw IP endpoint without clear disclosure, trust validation, or normal platform safeguards materially increases privacy, integrity, and supply-chain risk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill enables network, file, and environment-backed execution via a helper script but does not declare any tool scope or permission boundaries. That creates an authorization gap where an agent may use broader capabilities than a user would reasonably expect from a resume-assistance skill, increasing the risk of unintended data access or exfiltration.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The description says to use the skill whenever a user wants help 'building, importing, rewriting, tailoring, or improving a resume' and related tasks, which spans many common user intents. Because it does not specify constraints or exclusions, the trigger scope is broad enough to overlap with ordinary conversation and other resume-assistance skills.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The phrase 'Use this skill when the user wants resume help and HiHired is the right execution path' is ambiguous because 'resume help' is a very broad class of everyday requests, and the file does not define exclusion conditions for when another workflow should be used instead. This increases the chance of unintended invocation for general career-advice or non-HiHired resume conversations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill directs agents to send resumes and job descriptions to real HiHired endpoints without requiring disclosure or consent, even though those inputs routinely contain sensitive personal and employment data. Silent transmission of this data to a third-party service creates a meaningful privacy and compliance risk, especially when combined with direct backend access.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs agents to bypass the normal user-facing domain and call a raw backend IP address directly. Direct-to-IP routing evades expected trust and security controls such as domain-based validation, WAF/CDN protections, certificate expectations, and user visibility, making sensitive resume data harder to audit and easier to misroute or intercept.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill encourages use of a resume parsing endpoint for uploading resume files and extracting structured data, but it provides no warning that highly sensitive personal data may be transmitted to an external service. Resumes commonly contain full names, email addresses, phone numbers, employment history, and education details, so omitting disclosure and consent language can lead to unintended exposure of personal information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script uploads resumes and related text to a remote API endpoint, and resumes commonly contain highly sensitive personal data such as full name, contact details, employment history, and education. In this skill context, the default base URL is plain HTTP to a raw IP address, which further increases the danger because the data may be sent without transport encryption and without clear user disclosure.

Content

No source excerpt is available for this finding.