Back to skill

Security audit

HR助手

Security checks for vulnerabilities and agentic risk

Overview

This HR assistant is broadly coherent, but it needs Review because it can immediately change or delete sensitive employee records and persist HR, payroll, and conversation data locally without strong safety controls.

Install only in a trusted, access-controlled environment. Back up HR spreadsheets before use, protect the .hr-data directory, avoid using it on shared machines, and require manual review before employee deletion, bulk status changes, payroll runs, or exports. Treat generated Excel files carefully until formula-injection hardening and stronger confirmation controls are added.

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)

T09 · Insecure Skill Coding Practices

Error
Location
tools/employee_manager.py:650
Finding
Spreadsheet Formula Injection Through Employee Fields<![CDATA[ ## Vulnerability Details **File Location**: `tools/employee_manager.py:650-672` **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: High ### Vulnerable Code ```python for attrName, defaultHeader in fieldOrder: # Find the corresponding column mappedColumn = self._columnMapping.get(attrName) targetColumn = mappedColumn if mappedColumn else defaultHeader # Find the column index if targetColumn in self._headerRow: colIdx = self._headerRow.index(targetColumn) + 1 else: # Use the default order when the column is unavailable colIdx = fieldOrder.index((attrName, defaultHeader)) + 1 value = getattr(emp, attrName, "") if isinstance(value, str) and not value: value = None elif isinstance(value, float) and value == 0: value = None ws.cell(row=rowIdx, column=colIdx, value=value) ``` The validation performed before saving employee records does not reject or neutralize formula-leading strings: ```python def addEmployee(self, emp: Employee) -> Tuple[bool, str]: # Validation isValid, errors = emp.validate() if not isValid: return False, f"Employee validation failed: {'; '.join(errors)}" # Check employee-number uniqueness if emp.empNo in self.employees: return False, f"Employee number already exists: {emp.empNo}" if not emp.status: emp.status = "probation" emp._isNew = True self.employees[emp.empNo] = emp ``` ### Technical Analysis Employee properties are passed directly to `openpyxl` worksheet cells. A string beginning with `=` is interpreted as a formula rather than ordinary text. Other spreadsheet clients may also interpret strings beginning with `+`, `-`, or `@` as formulas or formula-like expressions. The employee validation routine checks required values and selected phone, email, identity-card, and date formats. It does not sanitize free-form fields such as employee name, department, position, rep ...[truncated 1680 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a centralized spreadsheet-output sanitizer and use it for every untrusted string: ```python def sanitize_excel_text(value): if isinstance(value, str) and value.startswith(("=", "+", "-", "@")): return "'" + value return value ``` 2. Apply the sanitizer before every workbook write: ```python value = sanitize_excel_text(value) ws.cell(row=rowIdx, column=colIdx, value=value) ``` 3. Force untrusted textual cells to use a text format where appropriate: ```python cell = ws.cell(row=rowIdx, column=colIdx) cell.number_format = "@" cell.value = sanitize_excel_text(value) ``` 4. Apply equivalent protection to: - Employee fields. - Department fields. - Custom and extra fields. - Workbook headers. - Attendance data. - Payroll and report exports. 5. Add tests covering values beginning with `=`, `+`, `-`, and `@`, including values preceded by whitespace or control characters. 6. Do not rely solely on input validation. Sanitize at the final spreadsheet-output boundary so imported data and values created by other APIs receive the same protection. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
tools/hr_store.py:584
Finding
Path Traversal in Conversation Storage<![CDATA[ ## Vulnerability Details **File Location**: `tools/hr_store.py:584-617` **Vulnerability Type**: Path traversal and arbitrary JSON file access **Risk Level**: High ### Vulnerable Code ```python def _conversationPath(self, sessionId: str) -> str: return os.path.join(self.dataDir, "conversations", f"{sessionId}.json") def saveConversation(self, sessionId: str, turns: List[Dict]) -> bool: data = { "sessionId": sessionId, "updatedAt": datetime.now().isoformat(), "turnCount": len(turns), "turns": turns, } try: with open(self._conversationPath(sessionId), 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) return True except IOError: return False def loadConversation(self, sessionId: str) -> Optional[Dict]: path = self._conversationPath(sessionId) if not os.path.exists(path): return None try: with open(path, 'r', encoding='utf-8') as f: return json.load(f) except (json.JSONDecodeError, IOError): return None ``` ### Technical Analysis `sessionId` is concatenated into a filesystem path without validation, normalization, or a containment check. Path components such as `../` can therefore escape the intended `.hr-data/conversations` directory. The method appends `.json`, which limits the most direct attack to paths ending in that suffix. It does not, however, prevent reading or overwriting JSON files elsewhere when the process has the required filesystem permissions. Although the current command-line entry point does not directly expose a session identifier, these methods form a public storage API. Any current or future integration that passes an externally controlled session identifier to them inherits the vulnerability. ### Attack Path 1. An attacker obtains control over a session identifier passed to `HRStore.saveConversation()` or `HRStore.loadConversation()`. 2. The attacker submit ...[truncated 1108 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict session identifiers to a conservative allowlist: ```python import re SESSION_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,128}$") def validate_session_id(session_id): if not SESSION_ID_PATTERN.fullmatch(session_id): raise ValueError("Invalid session identifier") ``` 2. Resolve and verify the final path remains beneath the conversation directory: ```python def _conversationPath(self, sessionId: str) -> str: validate_session_id(sessionId) base = os.path.realpath(os.path.join(self.dataDir, "conversations")) candidate = os.path.realpath(os.path.join(base, sessionId + ".json")) if os.path.commonpath([base, candidate]) != base: raise ValueError("Conversation path escapes storage directory") return candidate ``` 3. Reject path separators, absolute paths, traversal segments, null characters, and platform-specific alternate separators. 4. Generate server-side random session identifiers instead of accepting arbitrary identifiers when possible. 5. Open new files safely and consider atomic replacement for updates: - Write to a temporary file inside the same controlled directory. - Set restrictive permissions. - Atomically replace the destination. 6. Add tests for `../`, absolute paths, encoded separators, Windows separators, long identifiers, and symbolic-link edge cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
tools/intent_router.py:2415
Finding
Destructive HR Operations Execute Without Required Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `tools/intent_router.py:2415-2441` **Additional Locations**: `tools/intent_router.py:1100-1164`, `tools/intent_router.py:1281-1290`, `tools/intent_router.py:1428-1447`, `SKILL.md:260-264` **Vulnerability Type**: Missing confirmation and authorization state for destructive operations **Risk Level**: Medium ### Vulnerable Code The request-processing flow executes an operation immediately after checking only for missing parameters: ```python # Check required parameters missing = self.extractor.checkMissingParams(intent_result.intent, params) if missing: self.context.pendingAction = { "intent": intent_result.intent, "params": params, "missingParams": missing, } self.context.lastIntent = intent_result.intent followup = self.formatter.askForParams(missing, intent_result.intent) self.context.addTurn("assistant", followup, intent_result.intent) return { "success": False, "message": followup, "intent": intent_result.intent.value, "confidence": intent_result.confidence, "needsInput": True, "extractedParams": params, } # Execute the tool result = self.executor.execute(intent_result.intent, params) ``` The executor dispatches destructive intents directly: ```python executor_map = { IntentType.RESET_CONFIG: self._exec_reset_config, IntentType.ADD_EMPLOYEE: self._exec_add_employee, IntentType.UPDATE_EMPLOYEE: self._exec_update_employee, IntentType.DELETE_EMPLOYEE: self._exec_delete_employee, IntentType.BATCH_UPDATE_STATUS: self._exec_batch_update_status, } ``` Employee deletion then modifies and saves the workbook immediately: ```python def _exec_delete_employee(self, params: Dict) -> Dict: mgr = self.employeeManager if not mgr: return {"success": False, "message": "Employee roster is not configured"} emp_no = params.get("empNo") soft = params.get("soft", True) ...[truncated 2444 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define an explicit set of operations requiring confirmation: - Employee creation. - Employee updates. - Employee deletion or separation. - Batch changes. - Configuration reset. - Payroll calculation when it writes persistent results. - Any report operation that overwrites an existing file. 2. Store a pending operation rather than executing immediately: ```python pending = { "operation_id": generated_id, "intent": intent.value, "params": normalized_params, "created_at": timestamp, "expires_at": expiry, } ``` 3. Return a non-mutating preview that clearly identifies: - The operation. - Target employee or file. - Fields that will change. - Whether the operation is reversible. - The exact confirmation phrase or operation identifier. 4. Execute only after a separate message explicitly confirms the same pending operation. 5. Bind confirmations to a canonical digest of the intent and parameters so an attacker cannot alter parameters between preview and execution. 6. Expire pending operations after a short period and invalidate them after any unrelated request. 7. Require stronger confirmation for batch operations, physical deletion, configuration reset, and overwriting payroll history. 8. Add tests proving that destructive intents do not modify files before confirmation and that stale or mismatched confirmations are rejected. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:76
Finding
Unpinned Third-Party Dependencies Create a Non-Reproducible Supply Chain<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:76-79` **Additional Location**: `README.md:30` **Vulnerability Type**: Unpinned dependencies **Risk Level**: Low ### Vulnerable Configuration ```yaml packages: - openpyxl>=3.0 - xlrd>=2.0 ``` The installation documentation also resolves unspecified current releases: ```bash pip install openpyxl xlrd ``` ### Technical Analysis The Skill uses lower-bound dependency constraints without an upper bound, lockfile, or package hashes. Every installation can therefore resolve to a different release. The package names correspond to ordinary public packages, and the audit found no suspicious custom package index, typosquatted name, or dependency-installation script. The weakness is that future and unreviewed releases are implicitly trusted and builds cannot be reproduced reliably. ### Attack Path 1. A user installs the Skill dependencies using the documented command or metadata. 2. The package resolver selects the newest releases satisfying the broad constraints. 3. A future compromised, malicious, or incompatible dependency release is downloaded. 4. Dependency code executes during installation, import, or workbook processing with the privileges of the Skill environment. 5. The resulting behavior differs from the version originally reviewed. ### Impact Assessment Potential impact is limited to a future dependency compromise or incompatible release, but could include: - Execution of dependency code with the Skill process privileges. - Unauthorized access to local HR and payroll files available to the process. - Corruption or incorrect parsing of Excel workbooks. - Non-reproducible deployments and difficult incident investigation. No currently bundled dependency was demonstrated to be malicious. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin dependencies to reviewed versions rather than broad lower bounds. 2. Provide a lockfile generated through a reproducible dependency-management process. 3. Require package hashes, for example through a hash-locked requirements file: ```text openpyxl==<reviewed-version> --hash=sha256:<reviewed-hash> xlrd==<reviewed-version> --hash=sha256:<reviewed-hash> ``` 4. Install dependencies only from a trusted, explicitly configured package index. 5. Use automated dependency vulnerability scanning and controlled update reviews. 6. Test new dependency versions against representative untrusted and malformed workbook samples before updating the lockfile. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (49)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The description promises a broad HR assistant including payroll, tax, attendance, bonus optimization, and Excel/JSON-based local storage. The supplied code only covers the employee management portion: employee/department data models, Excel import/export, CRUD, validation, organizational hierarchy support, and simple statistics/reminders. While some payroll-related fields exist on the Employee model (e.g., base salary, social insurance base, housing fund rate, special deduction), there is no calculation logic for payroll, tax, deductions, or optimization. There is also no attendance handling and no JSON read/write behavior in this chunk. Therefore the code materially under-delivers versus the declared purpose, making the description inaccurate for this supplied code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding
The code is clearly related to HR data management, so it partially aligns with the roster and organizational-structure portions of the description. However, this specific chunk only contains unit tests for employee/department management via Excel files. Its primary observable behavior is testing CRUD, validation, org trees, reporting chains, and column mappings. The broader declared description promises substantial payroll, tax, attendance, bonus-optimization, reporting, and Excel+JSON storage capabilities, none of which are evidenced in this code chunk. Because the actual chunk is materially narrower than the declared purpose, this should be flagged as a mismatch.

Missing User Warnings

High
Confidence
94% confidence
Finding
The skill exposes destructive operations such as delete_employee with physical deletion and bulk status changes without surfacing an explicit warning in the manifest text about irreversible effects. In HR workflows, accidental or misunderstood destructive actions can permanently remove employee records or alter employment status, with downstream payroll and compliance consequences.

Vague Triggers

High
Confidence
97% confidence
Finding
Batch-operation triggers such as '批量更新' and '批量操作' are ambiguous and can map users into workflows capable of mass status changes, including bulk resignation or conversion. In an HR system, accidental activation of such destructive operations can corrupt workforce records at scale and may affect payroll, reporting, and audit integrity.

Missing User Warnings

High
Confidence
97% confidence
Finding
`_exec_delete_employee` deletes or soft-deletes an employee record without any explicit confirmation, review step, or warning. Because intent classification is pattern-based and can infer identifiers from short Chinese strings, an accidental or maliciously phrased prompt could remove personnel records, impacting HR operations, payroll accuracy, and auditability.

Missing User Warnings

High
Confidence
97% confidence
Finding
`_exec_batch_update_status` can mark multiple employees as 离职 or otherwise change status in bulk with no user confirmation, preview, or safety interlock. In an HR/payroll context, bulk offboarding can immediately corrupt workforce status, downstream payroll, attendance, and reporting for many people at once, making this more dangerous than a single-record action.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README states that all data is stored locally and lists a conversations history directory, but it does not clearly warn users that highly sensitive HR and payroll data, including chat transcripts, will persist on disk. In an HR skill handling personal, compensation, attendance, and tax data, this omission can lead to unsafe deployment on shared machines or unmanaged endpoints where other local users, backups, or endpoint tools may access the files.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The interaction rules explicitly state '所有响应使用中文', which forces a specific language for all responses. This is a natural-language policy concern because the file does not offer the user a language choice or describe this as an opt-in setting.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The onboarding flow explicitly asks users to upload employee roster and payroll spreadsheets containing highly sensitive personal and compensation data, but it does not present any privacy notice, data-minimization guidance, consent checkpoint, or warning about handling local file paths and confidential records. In an HR skill, this omission increases the risk of users exposing regulated personal data without understanding retention, access, masking, or safe-sharing expectations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The manifest description markets local storage as a feature but does not explicitly warn that highly sensitive HR, payroll, and conversation data will be stored on disk. This creates a transparency and privacy risk because users may bind regulated personal data without understanding the local retention footprint or the need to protect those files.

Vague Triggers

Medium
Confidence
91% confidence
Finding
Initialization and binding patterns like '初始化', '设置数据', '绑定表格', '用excel', and '上传表格' are broad enough to collide with routine conversation, potentially kicking off configuration or file-binding workflows unexpectedly. Because this skill has filesystem read/write permissions and processes local HR files, mistaken activation can lead to unintended file access or configuration changes.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The read_employee intent includes very broad trigger phrases such as '查看', '工号', '的信息', and '的详情', which are common in ordinary conversation and can easily cause unintended activation of employee data retrieval. In an HR skill handling sensitive personnel data, accidental intent routing can expose private employee records without the user clearly intending a lookup.

Ssd 3

Medium
Confidence
96% confidence
Finding
The system prompt specifies persistent storage of full conversation history in .hr-data/conversations/<sessionId>.json, and those conversations are likely to contain sensitive HR and payroll data. Persisting raw conversational transcripts unnecessarily increases the exposure surface for personal information, salary details, disciplinary matters, and other regulated employee data if local files are accessed, copied, or improperly retained.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The instruction "使用中文回复" imposes a fixed language policy. Under SQP-3, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly documented and justified as region-specific.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code supports irreversible deletion when `soft=False` by removing the employee from the in-memory dataset, and subsequent wrapper flows call `save()` to rewrite the workbook. While there are docstrings describing the parameter, there is no user-facing confirmation prompt, warning, or visible disclosure at the point of destructive action.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The wrapper performs a batch status change and automatically saves the modified roster when any update succeeds. Marking employees as `离职` changes personnel records in bulk, but the function provides no visible warning, confirmation, or disclosure that a file write affecting multiple employee records will occur.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The writeData function loads an existing workbook, deletes all rows in an existing sheet, and saves back to the same file path, which can overwrite prior user data. Although the docstring says it writes data, there is no explicit warning, confirmation, or user-facing disclosure that existing sheet contents may be cleared and replaced.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This helper reads spreadsheet rows and returns full row data, while the adapter is clearly designed for HR datasets containing fields such as phone numbers, email addresses, ID card numbers, and salary information. The code includes no disclosure, warning, or comment reminding callers that the operation may process and expose sensitive personal information.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The module’s natural-language documentation is entirely in Chinese and presents the skill behavior in that language without indicating any user choice or locale constraint. Under the stated policy, forcing a specific language without opt-in is a reportable natural-language policy violation.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code writes monthly payroll results, including per-employee compensation data, to local JSON files without any mechanism here to ensure the user was warned or consented at the point of storage. Because this skill handles sensitive HR and salary information, silent persistence increases privacy and compliance risk if the host machine is shared, backed up insecurely, or later accessed by unauthorized users.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The store persists full conversation histories to local JSON files under the conversations directory, but the skill description focuses on HR records, payroll, attendance, and reports rather than retention of free-form dialogue content. In an HR assistant, conversations can contain highly sensitive personal and payroll information, so undisclosed retention expands the data footprint and privacy risk beyond the stated scope.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The saveConversation method stores complete dialogue turns to disk without any explicit disclosure, consent, or retention limit. In an HR context, users may share identity data, compensation details, disciplinary matters, or other sensitive personnel information in natural language, making undisclosed transcript storage materially risky.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
`_exec_reset_config` performs destructive configuration reset immediately after intent matching, with no confirmation gate, warning, or undo flow. In an HR skill with filesystem write access, a mistaken or ambiguously classified request could wipe bindings and operational setup, causing disruption and potential loss of access to business-critical payroll/employee data workflows.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
Batch payroll calculation automatically persists payroll results to local storage via `savePayrollResult` without explicit disclosure or opt-in in this execution path. In an HR system handling sensitive salary and tax data, silent persistence increases privacy/compliance risk and can leave confidential payroll artifacts on disk longer than users expect.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The attendance-rules handler returns a read-only description of `AttendanceRules.default()` and never applies any user-supplied changes, even though the surrounding intent patterns and help text explicitly include phrases like “设置/修改/调整考勤规则” and “如需修改规则,可直接告诉我,如「迟到每次扣100元」”. That is an active contradiction between documented behavior and implemented behavior.

Static analysis

No suspicious patterns detected.