Back to skill

Security audit

Job Search Tailor

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent job-search purpose, but it should be reviewed because it persists resume-derived data and lets untrusted job pages and configurable paths influence local file writes.

Install only if you are comfortable storing resume-derived files and job history locally under ~/.job-search. Review config.json paths before running, keep Google Docs disabled unless you deliberately set up credentials later, and manually approve any newly created archetype or resume file before relying on it.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (2)

T01 · Skill Instruction Hijacking

Warning
Location
SKILL.md:151
Finding

Untrusted Job-Page Content Is Processed Without Prompt-Injection Isolation

Content
View full analysis
.md` c. Extract 4–6 keywords from the job title and description d. Call: ``` python3 ~/.openclaw/workspace/skills/job-search-tailor/scripts/save_archetype.py \ --name "" \ --keywords "" \ --resume-path "~/.job-search/archetypes/.md" ``` ``` ### Technical Analysis The Skill directs the agent to retrieve and interpret content from externally controlled job pages. It does not instruct the agent to treat fetched page content strictly as untrusted data, ignore instructions embedded in that content, or validate extracted fields against a fixed schema. Fetched job titles and descriptions subsequently influence: - Archetype selection. - Generation of new archetype names and keywords. - Resume file content. - Arguments supplied to `save_archetype.py`. - Persistent configuration records. A malicious job listing can include text crafted as agent instructions rather tha ...[truncated 1779 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update_tracking.py:72
Finding

Unrestricted Tracking Path Allows Same-User File Clobbering and Symlink Following

Content
View full analysis
= cutoff: seen_recently.add(url) # Filter to new-only URLs new_urls = [u for u in incoming_urls if u not in seen_recently] # Append new URLs to tracking records with today's date today_str = today.isoformat() for url in new_urls: records.append({"url": url, "shared_date": today_str}) # Persist updated tracking file try: save_tracking(tracking_path, records) except OSError as e: print(f"Warning: could not save tracking file: {e}", file=sys.stderr) ``` The write helper follows the supplied path directly: ```python def save_tracking(tracking_path: Path, records: list) -> None: """Write tracking records to file, creating parent dirs as needed.""" tracking_path.parent.mkdir(parents=True, exist_ok=True) with tracking_path.open("w", encoding="utf-8") as f: json.dump(records, f, indent=2) ``` ### Technical Analysis The `--tracking-file` argument is accepted as an unrestricted pa ...[truncated 2389 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • 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 (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad job-search and resume-matching skill, with LinkedIn job discovery, deduplication, automatic archetype matching/creation, and onboarding behavior. The supplied code does none of that. It is narrowly scoped to registering or updating an archetype entry in a local configuration file. While archetype storage could be a supporting component of the larger skill, this code chunk’s actual purpose is materially narrower and does not implement the primary declared behavior. Therefore this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code implements only one narrow support function from the broader description: deduplicating URLs against a tracking file. It does not search LinkedIn, discover jobs, process resumes, cluster archetypes, match jobs to resumes, create new archetypes, or handle first-run setup and preferences. While deduplication is mentioned in the declared purpose, the actual code chunk’s primary behavior is materially narrower than the declared skill, so the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents an end-user automation skill for searching jobs and tailoring resumes. The actual code chunk is a Python unittest file focused on validating three scripts' CLI behavior. From the tests, the underlying covered capabilities appear limited to config loading, URL dedup tracking, and archetype config persistence. Those are supporting pieces of the declared system, but the main promised functionality—searching LinkedIn for jobs, matching jobs to best-fit archetypes, creating archetypes on the fly based on matching, and bootstrapping by asking the user for resume/roles/locations/preferences—is absent from this code chunk. Because the supplied code's primary purpose is materially different and omits the headline capabilities, this is a mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill explicitly instructs use of shell commands, file reads, and file writes, but it does not declare any tool scope or permissions boundaries. That makes the skill over-privileged by default and prevents meaningful policy enforcement or user review of sensitive operations such as writing resume data and config files under the home directory.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Broad trigger phrases like "job search" or "find me jobs" can cause unintended activation in normal conversation. Because this skill can read local config, process resume content, and write files, accidental invocation could expose or persist sensitive personal data without clear user intent.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill creates persistent resume archetypes derived from the user's resume and fetched job descriptions, which is a form of session persistence involving sensitive personal and professional data. Persisting transformed resume content across runs expands exposure surface and can retain more inferred information than the user realizes, especially when combined with ongoing tracking files.

Content

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

md
For each result URL, `web_fetch` the full page to extract:
- Job title, company, location, salary (if shown), full job description

### A3. Create archetypes

Analyze the user's resume text alongside 3–5 of the fetched job descriptions.
Identify 3–5 natural clusters of role types that appear in the JDs and align with

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill stores sensitive resume content, tailored resume variants, configuration, and job-tracking history under the user's home directory, but it does not clearly warn the user beforehand. Resume data often contains PII, employment history, contact details, and other sensitive information, so silent persistence materially increases privacy and data-handling risk.

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 · assets/config.example.json (reported line 28)May include surrounding context.

json
"_comment_archetypes_dir": "Folder where archetype resume markdown files are stored.",
  "archetypes_dir": "~/.job-search/archetypes/",

  "_comment_threshold": "Min keyword match score (0–1) to use an existing archetype. Below this → create new.",
  "archetype_match_threshold": 0.5,

  "_comment_google_docs": "Set true to push new archetypes to Google Docs. Requires OAuth setup.",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The guide instructs users to place Google service account credentials or OAuth tokens in a predictable local path and describes uploading resume archetypes to Google Docs, but it omits explicit warnings about the sensitivity of those credentials and the privacy implications of document upload and sharing. In a job-search skill that handles resumes and potentially personal data, this can lead users to store secrets insecurely or expose sensitive career information through misconfigured document sharing.

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 · tests/test_scripts.py (reported line 48)May include surrounding context.

python
def run(script, args, cwd=None):
    """Run a script as a subprocess and return (stdout, stderr, returncode)."""
    result = subprocess.run(
        [sys.executable, script] + args,
        capture_output=True,
        text=True,