Back to skill

Security audit

Hwp Batch Convert Repo

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real HWP batch converter, but its optional auto-approval feature can click security prompts using broad desktop-window matching.

Review before installing. Use this only for trusted HWP/HWPX files on a Windows machine with Hancom HWP installed. Avoid --auto-allow-dialogs for untrusted documents or shared desktop sessions because it may approve a matching security prompt without manual review. Prefer --plan-only first, choose a controlled output folder, and check JSON reports for any auto-dialog events.

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/hwp_batch_convert.py:333
Finding
Global Dialog Watcher Can Approve Spoofed or Unrelated Security Prompts## Vulnerability Details **File Location**: `scripts/hwp_batch_convert.py`, lines 333–363 **Vulnerability Type**: Improper authorization of UI automation targets **Risk Level**: Medium ### Vulnerable Code ```python def _scan_once(self) -> bool: matched = False hwnds: list[int] = [] enum_proc = ctypes.WINFUNCTYPE( wintypes.BOOL, wintypes.HWND, wintypes.LPARAM, )(lambda hwnd, lparam: hwnds.append(hwnd) or True) USER32.EnumWindows(enum_proc, 0) for hwnd in hwnds: if hwnd in self._handled_hwnds or not USER32.IsWindowVisible(hwnd): continue title = USER32.GetWindowTextLengthW(hwnd) if title <= 0: continue window_title = ctypes.create_unicode_buffer(title + 1) USER32.GetWindowTextW(hwnd, window_title, title + 1) if window_title.value.strip() not in DIALOG_TITLE_WHITELIST: continue text_parts, allow_button_hwnd, allow_button_text = self._inspect_dialog(hwnd) window_text = ' '.join(part for part in text_parts if part).strip() reason = self._classify_candidate( window_title.value.strip(), window_text, allow_button_text, ) if reason != 'match': if window_text: self._record_event( AutoDialogEvent( window_title=window_title.value.strip(), window_text=window_text, button_text=allow_button_text, clicked=False, reason=reason, ) ) self._handled_hwnds.add(hwnd) continue clicked = False if allow_button_hwnd: USER32.SendMessageW(allow_button_hwnd, BM_CLICK, 0, 0) clicked = True ``` The application records newly created HWP process IDs in lines 403 and 429, but the dialog watcher does not use them: ```py ...[truncated 2659 chars]
Remediation
## Remediation Suggestions 1. Use `GetWindowThreadProcessId` to retrieve the process ID of every candidate top-level window. 2. Pass the converter's verified process identity to `AutoAllowDialogWatcher` and reject any window whose owner is not in `RealHwpConverter.owned_pids`. 3. Verify the process image path and expected Hancom signature or installation path where feasible, rather than relying on the process name alone. 4. Fail closed when the HWP process cannot be uniquely identified. Do not enable automatic approval if process ownership cannot be established. 5. Bind eligible dialogs to the active conversion operation and, where possible, the specific document being opened. 6. Prefer a supported HWP automation security API or registered security module over generic desktop UI automation. 7. Require explicit user confirmation for ambiguous prompts or prompts belonging to pre-existing HWP processes. 8. Add negative tests that create matching dialogs from unrelated processes and verify that they are never clicked. 9. Update the documentation to clarify process-ownership guarantees and any remaining limitations of UI-based approval.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (11)

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README explicitly documents an option to automatically click a security confirmation popup that grants document access, which normalizes bypassing an interactive security control. Even with a narrow whitelist, this increases the chance that sensitive document access is silently approved during automation and can undermine user awareness and consent around privileged document operations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill exposes operational capabilities that imply reading files, writing outputs, and invoking shell commands, but it does not declare any explicit tool scope or permission boundary. In an agent environment, this can cause the skill to run with broader ambient authority than reviewers or orchestrators expect, increasing the risk of unintended filesystem access, command execution, or unsafe use on attacker-influenced paths.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The document explicitly describes auto-clicking a security confirmation dialog during HWP COM automation, which weakens a built-in user consent/security control. Even with a narrow whitelist, this can authorize document access or sensitive operations without an informed user decision, and the file does not prominently warn about the security tradeoff or require explicit opt-in acknowledgment.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _snapshot_hwp_pids() -> set[int]:
    try:
        result = subprocess.run(['tasklist', '/FO', 'CSV', '/NH'], capture_output=True, text=True, encoding='utf-8', errors='ignore', check=False)
        if result.returncode != 0:
            return set()
        import csv
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The auto-dialog watcher is designed to detect Hancom security confirmation dialogs and automatically click allow buttons, which weakens a built-in security control meant to require user approval for sensitive document access. In a batch-conversion context handling untrusted HWP/HWPX files, this increases the chance that risky document actions proceed without operator review.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
[void]$form.ShowDialog()
""".strip()
    watcher = AutoAllowDialogWatcher(enabled=True, poll_interval=0.2)
    proc = subprocess.Popen(['powershell', '-NoProfile', '-STA', '-Command', dialog_script])
    try:
        clicked = watcher.click_once_for_test(timeout_seconds=timeout_seconds)
        proc.wait(timeout=timeout_seconds)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script exposes '--auto-allow-dialogs' as a normal runtime option without a strong user-facing warning, even though it automates approval of security-sensitive prompts. That creates a safety issue because users may enable it for convenience and unknowingly suppress an important trust boundary when converting potentially malicious documents.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The README content is presented entirely in Korean, including usage guidance and warnings, with no indication that the skill is intentionally limited to Korean-speaking users or a Korea-specific compliance context. The policy requires avoiding forced language or locale constraints unless users are given a choice or the constraint is clearly documented and justified.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The instructions hard-code Korean window titles and button/body text such as '한글', '접근하려는 시도', '모두 허용', and '허용'. This creates a language/locale constraint in the skill behavior, but the file does not state that the skill is Korean-only by design or offer any language/locale choice.

Context-Inappropriate Capability

Low
Confidence
91% confidence
Finding
The manifest describes local HWP batch conversion via COM automation, but this helper mode launches PowerShell and creates a Windows Forms dialog solely to test the auto-click handler. Spawning an additional scripting runtime and GUI test harness is a broader capability than the declared document-conversion purpose.

Missing User Warnings

Low
Confidence
82% confidence
Finding
When --report-json is supplied, the code unconditionally writes to the specified path via write_text, which can overwrite an existing file. There is no confirmation prompt or runtime warning near the write operation to alert the user about this file-modifying behavior.

Static analysis

No suspicious patterns detected.