Back to skill

Security audit

Recruiting

Security checks for vulnerabilities and agentic risk

Overview

This recruiting skill keeps data local, but it stores applicant information and includes interview/reject recommendations that conflict with its promise not to make hiring decisions.

Install only if you are comfortable with applicant data and hiring assessments being stored locally in plaintext files, and do not use the skill's scores or interview/reject labels as automated hiring decisions. A safer version would require human approval for all candidate disposition changes, avoid definitive recommendations, minimize stored PII, and enforce private file permissions and retention/deletion practices.

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

Warning
Location
scripts/add_candidate.py:9
Finding

Candidate PII and Hiring Assessments Stored Without Enforced Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/add_candidate.py:9-27, 34-52; scripts/update_pipeline.py:8-20, 64-69
Vulnerability Type: Plaintext sensitive-data storage with ambient filesystem permissions
Risk Level: Medium

Vulnerable Code

scripts/add_candidate.py:9-27, 34-52

python
RECRUITING_DIR = os.path.expanduser("~/.openclaw/workspace/memory/recruiting")
PIPELINE_FILE = os.path.join(RECRUITING_DIR, "pipeline.json")

def ensure_dir():
    os.makedirs(RECRUITING_DIR, exist_ok=True)

def load_pipeline():
    if os.path.exists(PIPELINE_FILE):
        with open(PIPELINE_FILE, 'r') as f:
            return json.load(f)
    return {"candidates": []}

def save_pipeline(data):
    ensure_dir()
    with open(PIPELINE_FILE, 'w') as f:
        json.dump(data, f, indent=2)

parser.add_argument('--job-id', required=True, help='Job ID')
parser.add_argument('--name', required=True, help='Candidate name')
parser.add_argument('--email', required=True, help='Candidate email')
parser.add_argument('--source', default='Direct', help='Application source')

args = parser.parse_args()

candidate_id = f"CAND-{str(uuid.uuid4())[:6].upper()}"

candidate = {
    "id": candidate_id,
    "job_id": args.job_id,
    "name": args.name,
    "email": args.email,
    "source": args.source,
    "applied_at": datetime.now().isoformat(),
    "current_stage": "applied",
    "stage_history": [
        {"stage": "applied", "entered_at": datetime.now().isoformat(), "exited_at": None}
    ],
    "rating": "pending",
    "notes": ""
}

scripts/update_pipeline.py:8-20, 64-69

python
RECRUITING_DIR = os.path.expanduser("~/.openclaw/workspace/memory/recruiting")
PIPELINE_FILE = os.path.join(RECRUITING_DIR, "pipeline.json")

def load_pipeline():
    if not os.path.exists(PIPELINE_FILE):
        return {"candidates": []}
    with open(PIPELINE_FILE, 'r') as f:
        retu
...[truncated 2631 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create the recruiting directory with owner-only permissions and verify the mode even when it already exists:

    python
    os.makedirs(RECRUITING_DIR, mode=0o700, exist_ok=True)
    os.chmod(RECRUITING_DIR, 0o700)
    
  2. Write records through a securely created temporary file with mode 0600, flush and synchronize it, and atomically replace the destination:

    python
    import tempfile
    
    def save_pipeline(data):
        os.makedirs(RECRUITING_DIR, mode=0o700, exist_ok=True)
        os.chmod(RECRUITING_DIR, 0o700)
    
        fd, temp_path = tempfile.mkstemp(dir=RECRUITING_DIR)
        try:
            os.fchmod(fd, 0o600)
            with os.fdopen(fd, "w") as f:
                json.dump(data, f, indent=2)
                f.flush()
                os.fsync(f.fileno())
            os.replace(temp_path, PIPELINE_FILE)
            os.chmod(PIPELINE_FILE, 0o600)
        except Exception:
            try:
                os.unlink(temp_path)
            except FileNotFoundError:
                pass
            raise
    
  3. Apply equivalent protections to every file containing recruiting data, including job, candidate, interview, communication, and pipeline records.

  4. Add a migration or startup check that identifies and repairs overly permissive modes on existing directories and files. Where ACLs are supported, verify that they do not grant unintended access.

  5. Minimize stored personal information, define retention and deletion procedures, and avoid placing sensitive personal data in free-form notes.

  6. If organizational policy or the threat model requires protection from privileged local readers, use authenticated encryption at rest with keys stored separately from the recruiting files.

  7. Add automated tests that create files under a permissive test umask and assert that final directory and file modes remain 0700 and 0600, respectively.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (16)

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
70% confidence
Finding

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.

Content

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

md
## Disclaimer

This skill provides hiring process support only. All hiring decisions remain the responsibility of the hiring manager and organization. The skill does not guarantee candidate quality or success. Always comply with applicable employment laws and regulations.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The guidance moves beyond summarizing qualifications and makes concrete hiring decisions such as reject/interview outcomes and next-step prioritization. In an employment context, that creates a real risk that users will delegate judgment to the tool despite the manifest's stated limit, leading to unfair or non-compliant screening decisions and potential discrimination or regulatory exposure.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill explicitly directs the agent to read from and write to local files under memory/recruiting/ and to use scripts for data operations, but it does not declare any permissions or allowed-tools scope. This creates a capability/manifest mismatch that can cause the agent to operate with broader file access than is transparently documented, increasing the risk of unintended data access or modification involving candidate information.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The example trigger "Help me describe this position" is generic and not narrowly scoped to a specific command or context. Because it resembles ordinary assistance requests, it could cause unintended skill activation compared with the more specific examples on nearby lines.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The output example specifies "Remote (US timezones)", which constrains locale/timezone in natural language but does not explain that this is optional, user-selected, or justified by a region-specific requirement. Under the policy, forcing a locale constraint without opt-in or clear justification can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documented data structure stores personally identifiable information and hiring-related notes, including name, email, stage history, ratings, and freeform notes, but provides no privacy guidance, access controls, retention limits, or warning about handling sensitive candidate data. In a recruiting context, this increases the chance of improper collection, over-retention, unauthorized access, or noncompliant processing of applicant data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file presents candidate ratings, 'strong candidates' labels, and recommended actions such as moving candidates forward or rejecting them, which goes beyond neutral workflow tracking and can steer hiring decisions. This conflicts with the manifest claim that the skill never makes hiring decisions or replaces human judgment, creating a real risk that users rely on automated recommendations in a sensitive employment context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The content handles resumes, candidate IDs, assessments, and interview recommendations, all of which involve sensitive personal and employment-related data, yet it lacks prominent guidance on secure handling, minimization, and retention. In hiring contexts, weak privacy guardrails can lead to unnecessary exposure of PII and increase legal and compliance risk.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file presents itself as an assessment aid but elsewhere provides definitive recommendation categories and rejection logic, creating an internal contradiction that can mislead operators about the degree of automation involved. That mismatch increases the chance the tool will be used as a de facto decision-maker in a sensitive employment setting.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The invocation examples "Evaluate this candidate" and "Should we interview this person?" are generic natural-language requests that could appear in ordinary discussion, making activation scope unclear. The file does not provide constraints or negative examples to distinguish intentional skill invocation from normal conversation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documented flow recommends concrete actions such as scheduling interviews quickly and assigning applicant priority, which operationalizes hiring decisions rather than merely assisting with assessment. In hiring workflows, this can cause automated triage to shape candidate outcomes materially, undermining the stated requirement that the skill not replace human judgment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script stores candidate PII, including name and email, to a local JSON file without any notice, consent flow, or visibility to the user that personal data will be persisted. In a recruiting workflow this is sensitive employment-related information, and silent local retention increases privacy, compliance, and inadvertent disclosure risk if the workstation, home directory, or backups are accessible to others.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/create_job.py (reported line 2)May include surrounding context.

python
#!/usr/bin/env python3
"""Create a new job posting."""
import json
import os
import uuid

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code writes updated candidate pipeline data to disk by overwriting pipeline.json, which affects persisted recruiting records. Although the script prints a success message afterward, there is no warning, confirmation, or explanatory comment/docstring disclosing that it will modify stored candidate data before the write occurs.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The manifest says the skill should support workflow management and explicitly 'NEVER makes hiring decisions or replaces human judgment.' This script accepts and persists evaluative ratings such as 'strong', 'likely', 'maybe', and 'no_hire', which are decision-support judgments beyond neutral pipeline tracking and can materially influence hiring outcomes.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The docstring says 'Update candidate stage in pipeline,' but the implementation also updates next actions, action owners, and especially candidate ratings including 'no_hire.' While not a full contradiction, the inline documentation materially narrows the apparent intent compared with the actual evaluative updates performed by the script.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.