Back to skill

Security audit

CAJ转PDF·离线高保真版

Security checks across malware telemetry and agentic risk

Overview

This looks like a real local CAJ-to-PDF converter, but it asks users to trust automatic dependency installation, embedded Windows DLLs, and optional system-level Windows integration that are not cleanly scoped as an offline converter.

Install only if you are comfortable with a converter that may set up its own Python environment, download dependencies from a third-party PyPI mirror, extract embedded closed-source Windows DLLs, and includes optional registry integration. Treat it as a local document-conversion tool, avoid running the bundled install/context-menu command unless you want Windows registry changes, and use it only on documents and systems where those setup behaviors are acceptable.

SkillSpector

By NVIDIA
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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (64)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
JBigDecode_cc,
    decode_jbig2data_cc,
):
    dep_checker = run(["pkg-config", "--libs", "--cflags", "poppler"], stdout=PIPE)
    if dep_checker.returncode != 0:
        print("poppler 未安装,请通过系统包管理器安装", file=stderr)
        exit(-1)
Confidence
70% confidence
Finding
dep_checker = run(["pkg-config", "--libs", "--cflags", "poppler"], stdout=PIPE)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
jbigdec_cc,
        JBigDecode_cc,
    ]
    run(cmd_jbigdec)
    print("BUILD: {}".format(libjbigdec_so))

    poppler_flags = dep_checker.stdout.decode("utf-8").strip().split(" ")
Confidence
70% confidence
Finding
run(cmd_jbigdec)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
libjbig2codec_so,
        decode_jbig2data_cc,
    ]
    run(cmd_jbig2codec)
    print("BUILD: {}".format(libjbig2codec_so))
Confidence
70% confidence
Finding
run(cmd_jbig2codec)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
JBigDecode_cc,
    decode_jbig2data_x_cc,
):
    dep_checker = run(["pkg-config", "--libs", "--cflags", "jbig2dec"], stdout=PIPE)
    if dep_checker.returncode != 0:
        print("jbig2dec 未安装,请通过系统包管理器安装", file=stderr)
        exit(-1)
Confidence
70% confidence
Finding
dep_checker = run(["pkg-config", "--libs", "--cflags", "jbig2dec"], stdout=PIPE)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
jbigdec_cc,
        JBigDecode_cc,
    ]
    run(cmd_jbigdec)
    print("BUILD: {}".format(libjbigdec_so))

    jbig2dec_flags = dep_checker.stdout.decode("utf-8").strip().split(" ")
Confidence
70% confidence
Finding
run(cmd_jbigdec)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
libjbig2codec_so,
        decode_jbig2data_x_cc,
    ]
    run(cmd_jbig2codec)
    print("BUILD: {}".format(libjbig2codec_so))
Confidence
70% confidence
Finding
run(cmd_jbig2codec)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Use mutool to repair xref (optional; fall back if mutool unavailable)
        try:
            check_output(["mutool", "clean", "pdf.tmp", "pdf_toc.pdf"], stderr=STDOUT)
        except FileNotFoundError:
            # mutool not installed in this environment; keep the generated PDF as-is
            copy("pdf.tmp", "pdf_toc.pdf")
Confidence
70% confidence
Finding
check_output(["mutool", "clean", "pdf.tmp", "pdf_toc.pdf"], stderr=STDOUT)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Use mutool to repair xref (optional; fall back if mutool unavailable)
        try:
            check_output(["mutool", "clean", dest + ".tmp", dest], stderr=STDOUT)
        except FileNotFoundError:
            # mutool not installed in this environment; keep the generated PDF as-is
            copy(dest + ".tmp", dest)
Confidence
70% confidence
Finding
check_output(["mutool", "clean", dest + ".tmp", dest], stderr=STDOUT)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
pass
    else:
        try:
            utime = subprocess.check_output(["date", "--date", string, "+%s"])
        except subprocess.CalledProcessError:
            pass
        else:
Confidence
70% confidence
Finding
utime = subprocess.check_output(["date", "--date", string, "+%s"])

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
elif key == "indirect":
                pass
            else:
                self.content[getattr(MyPdfName, key)] = value

    def tostring(self):
        if self.stream is not None:
Confidence
50% confidence
Finding
self.content[getattr(MyPdfName, key)] = value

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
"last": "/Last",
        }
        for k, v in opt_keys.items():
            n = getattr(t["node"], k)()
            if n is not None:
                oli.update({PDF.NameObject(v): idorefs[n.index]})
        olitems.append(oli)
Confidence
50% confidence
Finding
n = getattr(t["node"], k)()

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if os.path.exists(vpy):
        return vpy
    print("[convert] venv not found, running setup.py ...")
    subprocess.run([sys.executable, os.path.join(SKILL_DIR, "setup.py")], check=True)
    if not os.path.exists(vpy):
        sys.exit("[convert] FATAL: venv still missing after setup.")
    return vpy
Confidence
70% confidence
Finding
subprocess.run([sys.executable, os.path.join(SKILL_DIR, "setup.py")], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 1) 确保运行在 venv 中
    vpy = ensure_venv()
    if sys.executable != vpy and os.path.exists(vpy):
        subprocess.run([vpy, __file__, *sys.argv[1:]], check=True)
        return

    # 2) 解析参数
Confidence
70% confidence
Finding
subprocess.run([vpy, __file__, *sys.argv[1:]], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 建 venv
    print("[setup] creating venv ...")
    subprocess.run([base_py, "-m", "venv", VENV], check=True)

    # 装依赖(阿里云镜像,国内快;pyproject 也指向此镜像)
    print(f"[setup] installing deps via {PYPI_MIRROR} ...")
Confidence
70% confidence
Finding
subprocess.run([base_py, "-m", "venv", VENV], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 装依赖(阿里云镜像,国内快;pyproject 也指向此镜像)
    print(f"[setup] installing deps via {PYPI_MIRROR} ...")
    subprocess.run([venv_pip(), "install", "-i", PYPI_MIRROR, *DEPS], check=True)

    # 校验
    subprocess.run([vpy, "-c", "import fitz, imagesize, PyPDF2; print('deps ok')"], check=True)
Confidence
70% confidence
Finding
subprocess.run([venv_pip(), "install", "-i", PYPI_MIRROR, *DEPS], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
subprocess.run([venv_pip(), "install", "-i", PYPI_MIRROR, *DEPS], check=True)

    # 校验
    subprocess.run([vpy, "-c", "import fitz, imagesize, PyPDF2; print('deps ok')"], check=True)
    print("[setup] DONE. caj-to-pdf environment is ready.")
Confidence
70% confidence
Finding
subprocess.run([vpy, "-c", "import fitz, imagesize, PyPDF2; print('deps ok')"], check=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 已存在且关键依赖(fitz)可用 -> 跳过
    if os.path.exists(vpy):
        try:
            subprocess.run([vpy, "-c", "import fitz, imagesize, PyPDF2"],
                           check=True, capture_output=True)
            print("[setup] venv already ready, nothing to do.")
            return
Confidence
70% confidence
Finding
subprocess.run([vpy, "-c", "import fitz, imagesize, PyPDF2"], check=True, capture_output=True)

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
主体功能与描述大体一致:代码支持 CAJ/KDH/HN 文献转 PDF,处理目录书签,提取内嵌 DLL,并用 PyMuPDF 修复输出 PDF,且是离线自包含实现。description 中提到的“闭源解码 DLL base64 内嵌、运行时提取”“PyMuPDF 修复”“批量转换”“失败兜底提示”都能在代码中找到对应实现。 但按评估标准,仍应判定为 mismatch,因为代码包含描述中未提及的额外能力:Windows 上的上下文菜单安装、注册表写入和管理员提权。这些属于系统配置/集成能力,不是单纯的 CAJ 转 PDF 功能,且是可由 CLI 直接触发的额外行为。虽然不是恶意隐藏行为,也与主用途相关,但它们确实超出了声明范围,因此应标记为不完全匹配。

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest repeatedly emphasizes '自包含离线' and '无需联网即可运行', but the documented behavior includes re-fetching source from GitHub and using an Alibaba Cloud package mirror for dependencies. Those are network-capable behaviors that exceed the plain meaning of an offline-only converter, even if presented as fallback or setup paths.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
A CAJ-to-PDF converter's stated purpose is local file conversion, and the manifest stresses that files are not uploaded and operation is offline. Documenting retrieval from GitHub and use of an external package mirror introduces network access capability unrelated to the core conversion task as presented.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The skill manifest frames the capability as an offline document conversion tool, but this README documents an additional behavior: installing a Windows right-click menu by writing registry entries. Persistent OS integration is not implied by 'convert CAJ/KDH/NH documents to PDF' and expands behavior beyond straightforward file conversion.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The manifest frames the skill as a self-contained offline CAJ/KDH/NH-to-PDF converter with bundled components. This file nonetheless spawns an external `mutool` process to repair PDFs, which is a subprocess execution capability not inherently justified by a document conversion skill as described, especially given the explicit self-contained claim.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The comment presents the behavior as benign temporary-file cleanup, but it explicitly documents bypassing the platform's safety wrapper around `os.remove` by calling `DeleteFileW` directly. That is an intent-level contradiction with the implied sandbox-respecting behavior and shows the code is deliberately circumventing host controls rather than simply deleting temporary files normally.

Context-Inappropriate Capability

Low
Confidence
86% confidence
Finding
The skill's stated purpose is converting literature files to PDF, but this parsing path emits additional forensic/debug artifacts for embedded images. Producing multiple side-output dump files is not justified by the declared converter purpose and expands behavior beyond the expected single converted document output.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

Detected: suspicious.obfuscated_code

Potential obfuscated payload detected.

Warn
Code
suspicious.obfuscated_code
Location
scripts/caj2pdf-restructured/caj2pdf/pdfwutils.py:1510