Back to skill

Security audit

Recruiter Assistant

Security checks for vulnerabilities and agentic risk

Overview

This recruitment skill is mostly aligned with resume screening, but it handles sensitive candidate data with unsafe command execution and directs public external sharing without clear safeguards.

Review before installing. Use only with trusted local files, avoid processing attacker-supplied filenames, and do not enable Feishu or messaging tools unless document visibility, recipients, consent, and candidate-data handling rules are explicitly controlled. The scripts should be fixed to avoid shell-based execSync with user-controlled input and to keep untrusted resume text separate from agent instructions.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/batch_screen.js:40
Finding

Shell Command Injection in Batch Resume Screening

Content
View full analysis

Vulnerability Details

File Location: scripts/batch_screen.js, lines 40-49
Vulnerability Type: OS command injection through shell command construction
Risk Level: Critical

Vulnerable Code

js
// Handle PDF conversion
if (file.toLowerCase().endsWith('.pdf')) {
  const tempTxt = path.join('/tmp', file + '.txt');
  execSync('pdftotext "' + filePath + '" "' + tempTxt + '"');
  processPath = tempTxt;
  isTemp = true;
}

const output = execSync('node ' + path.join(__dirname, 'screen_resume.js') + ' "' + processPath + '" --lang "' + lang + '" --yoe "' + yoe + '"', { encoding: 'utf8' });

Technical Analysis

The script concatenates several values into command strings passed to child_process.execSync, which invokes a system shell:

  • filePath and tempTxt are derived from filenames found in a user-selected directory.
  • lang comes from the user-controlled --lang argument.
  • yoe comes from the user-controlled --yoe argument.

Wrapping these values in double quotes does not make the operation safe. A value containing a quote, command substitution, or another shell-significant construct can terminate or alter the intended argument and introduce additional shell operations.

This is particularly dangerous for filenames because the batch workflow automatically enumerates files with accepted extensions. An attacker able to place a specially named PDF, text file, or Markdown file in the processed directory may reach the vulnerable command without controlling the command-line flags directly.

Attack Path

  1. The attacker supplies a resume with a shell-significant filename, or influences the --lang or --yoe option.
  2. The operator runs batch_screen.js against the affected directory or with the affected option.
  3. The script concatenates the untrusted value into an execSync command string.
  4. The system shell interprets the injected syntax rather than treating the entire value as a literal argument.
  5. The injected command e ...[truncated 749 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace execSync with execFileSync or spawnSync, passing each argument as a separate array element so no shell interprets user input.
  • Invoke the current Node executable through process.execPath rather than constructing a node shell command.
  • Validate --lang and --yoe against strict expected formats or allowlists.
  • Reject filenames containing control characters and enforce canonical path containment within the selected source directory.
  • Create temporary files through fs.mkdtempSync in a private temporary directory, using server-generated names instead of source filenames.
  • Ensure temporary files are cleaned in a finally block.
  • Run document conversion in a sandbox with minimal filesystem and network permissions.

A safer invocation pattern is:

js
const { execFileSync } = require('child_process');

execFileSync('pdftotext', [filePath, tempTxt], {
  stdio: ['ignore', 'pipe', 'pipe'],
  shell: false
});

const output = execFileSync(
  process.execPath,
  [path.join(__dirname, 'screen_resume.js'), processPath, '--lang', lang, '--yoe', yoe],
  {
    encoding: 'utf8',
    shell: false
  }
);

T09 · Insecure Skill Coding Practices

Error
Location
scripts/process_incoming.js:13
Finding

Command Injection and Path Traversal in Incoming File Processing

Content
View full analysis

Vulnerability Details

File Location: scripts/process_incoming.js, lines 13-37
Vulnerability Type: OS command injection, path traversal, and unsafe file deletion
Risk Level: Critical

Vulnerable Code

js
const localPath = path.join('/tmp', fileName);
const txtPath = localPath + '.txt';

console.log(`Processing: ${fileName}`);

// In a real environment with direct API access, we'd download here.
// For now, we simulate the logic assuming the file is accessible or
// will be provided to the screening script.

try {
    // If file exists (manually placed or downloaded by middleware)
    if (fs.existsSync(localPath)) {
        execSync(`pdftotext "${localPath}" "${txtPath}"`);
        const content = fs.readFileSync(txtPath, 'utf8');

        // We use the batch screen logic or direct screen
        const output = execSync(`node ${path.join(__dirname, 'screen_resume.js')} "${txtPath}" --lang "PHP Engineer" --yoe "10"`, { encoding: 'utf8' });

        console.log("--- EVALUATION RESULT ---");
        console.log(output);

        // Cleanup
        fs.unlinkSync(txtPath);

Technical Analysis

fileName is accepted directly from process.argv[3] and is used to construct paths under /tmp. The implementation does not reject absolute paths, path separators, traversal components, shell metacharacters, or quotation marks.

Although path.join('/tmp', fileName) normalizes the path, it does not verify that the resulting path remains inside /tmp. A traversal value can therefore resolve outside the intended directory. The resulting localPath and txtPath are then interpolated into commands executed by a system shell, creating a second vulnerability: command injection.

The script also calls fs.unlinkSync(txtPath) without first verifying that txtPath belongs to a temporary directory controlled by this workflow. Consequently, a crafted path can target an unintended .txt file for deletion after processing. The content variable is ...[truncated 1371 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not use the supplied filename as a filesystem path. Reduce it to a basename only if retaining the original name is necessary.
  • Resolve the path canonically and verify that it remains inside a dedicated, private working directory.
  • Reject absolute paths, .. components, path separators, null bytes, control characters, and unexpected extensions.
  • Replace both execSync calls with execFileSync or spawnSync using argument arrays and shell: false.
  • Use process.execPath to invoke the Node.js child process.
  • Generate the output filename independently rather than appending .txt to attacker-controlled input.
  • Before deleting a file, verify that its canonical path is contained within the workflow's temporary directory.
  • Place cleanup in a finally block and track only files created successfully by the current invocation.
  • Run PDF conversion with resource limits and minimal operating-system permissions.

Example containment check:

js
const workDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'resume-'));
const safeName = path.basename(fileName);

if (safeName !== fileName || !safeName.toLowerCase().endsWith('.pdf')) {
    throw new Error('Invalid filename');
}

const localPath = path.resolve(workDir, safeName);
if (!localPath.startsWith(workDir + path.sep)) {
    throw new Error('Path escapes working directory');
}

T01 · Skill Instruction Hijacking

Error
Location
scripts/screen_resume.js:28
Finding

Indirect Prompt Injection Through Untrusted Resume and Interview Content

Content
View full analysis

Vulnerability Details

File Locations:

  • scripts/screen_resume.js, lines 28-54
  • scripts/generate_questions.js, lines 18-21
  • scripts/summarize_interview.js, lines 18-21

Vulnerability Type: Indirect prompt injection and agent instruction hijacking
Risk Level: High

Vulnerable Code

Primary vulnerable flow in scripts/screen_resume.js:

js
const content = fs.readFileSync(resumePath, 'utf8');
console.log('--- SYSTEM CONFIGURATION ---');
console.log(`Target Language: ${lang}`);
console.log(`Experience Requirement: ${yoe} years`);
console.log('----------------------------\n');
console.log('--- RESUME CONTENT START ---');
console.log(content);
console.log('--- RESUME CONTENT END ---');
console.log(`\n[AGENT INSTRUCTION]: Evaluate this resume specifically for a ${lang} position with ${yoe} years of experience in Chinese (简体中文).

CRITICAL: You must evaluate the candidate's **AI Proficiency** (usage of Cursor, Copilot, LLM integration, etc.) as defined in references/hiring-criteria.md.

STRICT SCORING: Only experience relevant to ${lang} should contribute to the final score. Irrelevant tech stacks must not inflate the score. Data quantification is MANDATORY. Any resume without specific results (e.g., "reduced latency by 20%", "handled 10k QPS") MUST NOT score above 60.

MANDATORY SECTIONS:
- Evaluation Format: Use the exact structure and content defined in references/hiring-criteria.md.
- Mandatory Output Language: Chinese (简体中文).
- Detailed Pros: List at least 3-4 specific technical advantages.
- Detailed Cons: List at least 2-3 specific technical gaps or weaknesses.

ACTION PLAN:
1. Provide the evaluation in the requested format in Chinese.
2. IDENTIFY the name of the candidate.
3. CALL the feishu_doc tool with action="create" to generate a doc for the candidate.
4. CRITICAL: Use a clean, vertical layout (Headings and Paragraphs) in the Feishu doc. AVOID complex markdown/tables to ensure the content is visible.
5. PRESENT the final report 
...[truncated 3088 chars]
Remediation
View remediation

Remediation Suggestions

  • Explicitly classify all loaded document text as untrusted data that must never be interpreted as instructions.
  • Use structured model inputs with separate trusted-instruction and untrusted-data fields where the agent platform supports them.
  • Add a trusted instruction stating that commands, tool requests, links, or policy statements appearing inside candidate material must be ignored.
  • Parse only required resume fields before model submission rather than forwarding arbitrary raw content.
  • Restrict the model's tool access during document analysis. Separate analysis from external side effects into different execution stages.
  • Require explicit human approval before creating or sharing Feishu documents or invoking messaging tools.
  • Validate all tool arguments against a narrow schema generated from trusted workflow state, not from raw model output.
  • Apply output filtering for unexpected links, secret-like material, or tool directives.
  • Add adversarial tests containing instruction-like resume text to verify that scoring and tool behavior remain unchanged.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/screen_resume.js:49
Finding

Candidate Personal Data Is Directed to Public and External Sharing Without Safeguards

Content
View full analysis

Vulnerability Details

File Location: scripts/screen_resume.js, lines 49-54
Related Location: SKILL.md, lines 19-21
Vulnerability Type: Sensitive-data exposure through unsafe external sharing defaults
Risk Level: High

Vulnerable Code

From scripts/screen_resume.js:

js
ACTION PLAN:
1. Provide the evaluation in the requested format in Chinese.
2. IDENTIFY the name of the candidate.
3. CALL the feishu_doc tool with action="create" to generate a doc for the candidate.
4. CRITICAL: Use a clean, vertical layout (Headings and Paragraphs) in the Feishu doc. AVOID complex markdown/tables to ensure the content is visible.
5. PRESENT the final report and the public Feishu document link directly in the chat.
6. DO NOT use message attachments for the report.`);

Related workflow requirement from SKILL.md:

md
3. **Separate Reporting**: Each candidate evaluation must be saved/written to its own document.
4. **Salary Benchmark**: Compares the candidate's expected salary against Shenzhen market rates (Boss Zhipin 2026).
5. **HR Notification**: High-scoring candidates (>= threshold) should be summarized and sent to HR via the `message` tool.

Technical Analysis

The workflow instructs the agent to extract a candidate's identity, generate a Feishu document, expose a public document link in chat, and send qualifying candidate summaries through a messaging tool. Candidate resumes and evaluations may contain personal data, employment history, education, salary expectations, and evaluative hiring information.

No instruction requires:

  • candidate consent or a documented processing basis;
  • minimization or redaction of personal data;
  • confirmation of the HR recipient;
  • private document permissions;
  • access expiration;
  • tenant or organization restrictions;
  • human approval before publication or messaging.

The explicit requirement for a public link is inconsistent with least-disclosure handling of recruitment records. The issue ...[truncated 1436 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the requirement to create or present a public document link.
  • Create documents as private by default and restrict access to a validated HR group or explicitly approved recipients.
  • Require human confirmation before any resume-derived data is uploaded or messaged externally.
  • Minimize report content and redact contact details, government identifiers, addresses, and other data unnecessary for hiring decisions.
  • Validate message recipients against an organizational allowlist; never derive recipients from resume text or model output.
  • Implement retention periods, access expiration, audit logging, and revocation procedures.
  • Clearly inform users when processing causes data to leave the local environment.
  • Separate report generation from publication so a recruiter can review and approve the content and permissions first.
  • Apply applicable recruitment privacy and data-protection requirements before enabling external sharing.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code is related to resume screening, so it partially aligns with the declared recruitment-assistant theme. However, this specific chunk primarily implements batch file processing infrastructure: it reads a directory, filters .txt/.md/.pdf files, converts PDFs using pdftotext, and shells out to screen_resume.js for analysis. The declared description emphasizes resume evaluation, AI proficiency assessment, critical pros/cons analysis, and Shenzhen salary benchmarking, but none of that substantive analysis logic appears here. Additionally, the code has undeclared operational capabilities/resources such as directory traversal, temporary file handling in /tmp, and child-process execution of external tools/scripts. Because the actual behavior is materially more about batch document processing/orchestration than the declared end-user functionality, and because salary benchmarking is not represented in this chunk, this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description centers on candidate evaluation and salary assessment, but the supplied code does not implement any resume screening, scoring, benchmarking, or market-salary logic. Instead, it simply loads an existing screening result from a file and outputs an instruction to generate technical interview questions, specifically about Go/PHP and projects mentioned. That is a materially different primary purpose from the declared assistant behavior, so this is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared description promises a broader recruitment assistant that performs dynamic resume evaluation, critical pros/cons analysis, AI proficiency assessment, and Shenzhen-specific salary benchmarking. This code chunk does not implement those capabilities directly. Instead, it acts as an ingestion wrapper: it checks for a file in /tmp, converts it with pdftotext, and calls screen_resume.js with fixed arguments (--lang "PHP Engineer" and --yoe "10"). That is materially narrower than the declared purpose and contradicts the claim of dynamic requirements. There is also no implementation here of salary benchmarking, Shenzhen market logic, or AI proficiency analysis. Additionally, the code uses local file access and shell execution, which are operational capabilities not reflected in the description. While invoking screen_resume.js suggests some resume screening support, the overall behavior of this chunk does not accurately match the declared end-to-end assistant description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description says the skill evaluates resumes, analyzes pros/cons, and performs Shenzhen-specific salary benchmarking. The code chunk does not implement those core functions directly; instead, it reads a resume file and emits a prompt-like instruction block for another agent/tool to do the evaluation. It also includes an undeclared capability: calling a Feishu document tool to create a document and share a public link, which is not mentioned in the declared purpose or permissions. Additionally, the declared salary benchmarking capability is absent from the code. Because the actual behavior materially differs from the stated primary purpose and introduces an undeclared external-document action, this is a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a sophisticated recruitment assessment assistant focused on resume evaluation, technical screening, and salary benchmarking. The supplied code does none of those things. Instead, it is a simple command-line utility that reads a local notes file, prints the raw contents, and emits an instruction to summarize those notes according to a template. This is a materially different primary purpose. Additionally, the code accesses local files via a user-provided path, which is an operational capability not represented in the declared description. Therefore, the description does not accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This workflow combines two risky behaviors: full stdout disclosure of resume contents and subsequent instruction to publish candidate-related output via a public Feishu link. In the context of recruitment, the data is likely to contain extensive PII and employment history, making this especially dangerous and increasing the likelihood of privacy violations or regulatory noncompliance.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script explicitly instructs the agent to call a Feishu document creation tool and then present a public document link containing candidate-related evaluation output. This exceeds the stated recruitment screening purpose and creates an unauthorized data exfiltration path for sensitive resume information to an external service.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

Embedding external publishing behavior in a resume evaluation script introduces functionality unrelated to the core screening task and can disclose candidate PII outside the expected processing boundary. In a recruiting context, resumes commonly contain names, emails, phone numbers, and employment history, so unjustified publication is particularly sensitive.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The instruction to create and share a public Feishu document containing candidate evaluation material occurs without disclosure, consent, or confirmation from the user or the candidate. This can turn private hiring materials into publicly accessible content, amplifying privacy, compliance, and reputational risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly instructs sending high-scoring candidate summaries to HR via a messaging tool, but provides no consent, minimization, or warning about transmitting personal candidate data. In a recruitment context, resumes and evaluations commonly contain sensitive personal and employment information, so silent onward sharing increases privacy, confidentiality, and compliance risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill hard-codes a hiring-policy judgment that lack of AI tool usage is a significant productivity gap, without any user or organizational opt-in. In hiring workflows, fixed, undisclosed criteria can produce unfair or discriminatory screening outcomes, especially when they are not job-relevant for every role or employer policy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown file contains user-facing content exclusively in Chinese, including headings, evaluation criteria, and instructions. Under the policy rule for natural-language violations, forcing a specific language without user opt-in can exclude users and should be documented or made optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill mandates a Shenzhen-specific salary benchmark and a fixed evaluation/output format without indicating that this localization should be conditioned on user intent, candidate geography, or hiring market. In a recruitment workflow, this can systematically bias outputs toward one locale and compensation frame, causing misleading assessments or unfair screening when used for non-Shenzhen roles or diverse candidate pools.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The script runs pdftotext on user-supplied files via execSync, which is a subprocess operation covered by the missing-warning rule for code files. Although the script logs that it is processing files, it does not disclose that an external command will be executed or that temporary converted text will be created in /tmp.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script constructs shell commands by concatenating user-controlled values such as file paths, language, and years-of-experience into execSync calls. Even though some arguments are wrapped in quotes, this is still shell invocation of external tools and can lead to command injection or unsafe execution if quoting is bypassed or special characters are mishandled; it also expands the attack surface beyond the stated recruitment-analysis purpose.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script prints screening output directly to stdout immediately after processing resume files, and the surrounding markers indicate per-file content is being exposed in logs. In a recruitment context this is especially sensitive because resumes commonly contain PII, employment history, contact details, and other confidential candidate data that may be collected by terminals, CI logs, or centralized logging systems.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script invokes external commands via execSync to run both pdftotext and screen_resume.js. While the file logs that processing is happening, it does not clearly disclose to the user that shell commands will be executed on their file, and there is no confirmation prompt, explanatory comment for user impact, or referenced markdown warning in this file.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest says the skill evaluates resumes against dynamic requirements and AI proficiency, implying the job criteria should vary per request. The code always invokes the screening script with fixed parameters --lang "PHP Engineer" --yoe "10", which materially narrows and changes the advertised behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script prints the full resume contents directly to stdout, which may expose candidate personal and professional data in terminal history, logs, monitoring systems, or downstream agent transcripts. Because users are not warned and no masking is applied, sensitive information can be disclosed to operators or systems not intended to receive it.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The natural-language instruction requires the evaluation to be produced in Chinese (简体中文) and labels it as mandatory, with no option for user choice. This is a locale/language policy concern because the file forces a specific output language without opt-in or documented regional justification.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The inline comments describe a placeholder or simulated flow, yet the implementation immediately checks for a real file, runs pdftotext, reads the extracted text, and executes screen_resume.js. This documentation gives a materially misleading impression of the script's actual operational behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The script deletes the generated text file with unlinkSync after processing. Although cleanup is commented in code, there is no user-facing disclosure that an intermediate file will be created and then removed, which is a file write/delete operation covered by the warning requirement.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/batch_screen.js:44

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/process_incoming.js:27