PyPI / pypdfium2 / 5.15.0

pypdfium2 5.15.0

Source
https://github.com/pypdfium2-team/pypdfium2
Artifact scanned
pypdfium2-5.15.0.tar.gz (sdist · 685.0 KB · SHA-256 150f71bc385f…)
Compared with
pypdfium2-5.14.0.tar.gz (0 signals introduced since)
Files
153 scanned, 40 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 12 info
Scan time
10.4 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 11 of 11 warn and critical signals; 11 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-11-06 15:27

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info critical → info by review path-command-shadow README.md:244:1

    [embedded bash] redirect writes to `python` — filename matches a system command (PATH hijack setup) (ast pass, confidence 0.88)

    # assuming cross-compilation dependencies are installed
    > python setupsrc/build_toolchained.py --target-cpu arm
    > PDFIUM_PLATFORM=sourcebuild CROSS_TAG="manylinux_2_17_armv7l" python -m build -wxn
    > 
    likely benign LLM, confidence 0.90 Markdown build instructions showing a python invocation inside a blockquote.
    Reasoning

    The flagged 'redirect' is a markdown blockquote marker ('> python ...') in a documentation example for building pdfium bindings. It is not a shell redirect and not a PATH hijack.

  2. info warn → info by review dynamic-execution deps/ctypesgen/src/ctypesgen/parser/lex.py:215:13

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            else:
                exec('import %s' % tabfile)
                lextab = sys.modules[tabfile]
    likely benign LLM, confidence 0.85 Vendored PLY lexer dynamically imports its table module via exec.
    Reasoning

    This is PLY-style lexer code importing a generated lextab module by name, from a vendored ctypesgen dependency. The module name comes from internal configuration, not untrusted input.

  3. info critical → info by review frame-introspection deps/ctypesgen/src/ctypesgen/parser/lex.py:450:9

    `sys._getframe` — call-stack frame introspection — file also calls `sys.exit`/`exec`/`eval` (anti-analysis shape) (ast pass, confidence 0.85)

    def get_caller_module_dict(levels):
        f = sys._getframe(levels)
        ldict = f.f_globals.copy()
        if f.f_globals != f.f_locals:
    likely benign LLM, confidence 0.90 Vendored PLY helper uses sys._getframe to read caller namespaces.
    Reasoning

    get_caller_module_dict is the standard PLY helper for reading the caller's globals. The sys.exit/exec co-occurrence is incidental and this is not anti-analysis behaviour.

  4. info warn → info by review dynamic-execution deps/ctypesgen/src/ctypesgen/parser/lex.py:1036:21

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

                        pkgname = '.'.join(parts[:-1])
                        exec('import %s' % pkgname)
                        srcfile = getattr(sys.modules[pkgname], '__file__', '')
                outputdir = os.path.dirname(srcfile)
    likely benign LLM, confidence 0.85 Vendored PLY code imports a package by computed name to find its file path.
    Reasoning

    This is the same PLY pattern of exec('import %s' % pkgname) to locate a package's source directory. The name is derived from the package path the library itself computes.

  5. line length 11871 characters (mostly string/comment) (raw pass, confidence 0.39)

    _lexstatere   = {'INITIAL': [('(?P<t_ANY_directive>\\#\\s+(?P<lineno>\\d+)\\s+"(?P<filename>[^"]+)"[ \\d]*\\n)|(?P<t_ANY_f_const_1>(?P<sig>[0-9]+)(?P<exp>([Ee][+-]?[0-9]+))(f|F|l|L)?)', [None, ('t_ANY_directive', 'directive'), None, None, ('t_ANY_f_const_1', 'f_const_1'), None, None]), ('(?P<t_ANY_f_const_2>(?P<sig>[0-
  6. info warn → info by review long-line deps/ctypesgen/src/ctypesgen/parser/parsetab.py:11:1

    line length 92323 characters (raw pass, confidence 0.65)

    _lr_action_items = {'PP_DEFINE':([0,1,2,3,4,5,6,7,8,15,22,59,85,92,94,167,178,180,181,198,209,212,214,285,286,287,327,385,389,436,437,440,441,470,],[-1,12,-2,-3,-266,-267,-272,-273,-274,-287,-115,-271,-269,-270,-242,-275,-283,-284,-288,-268,-243,-244,-246,-276,-277,-278,-245,-279,-289,-280,-281,-290,-291,-282,]),'PP_UN
    likely benign LLM, confidence 0.92 Generated PLY parse table with an extremely long data line.
    Reasoning

    parsetab.py is a generated LALR parser table; very long lines of integer lists are expected. The raw long-line signal has a high false-positive rate here.

  7. info warn → info by review long-line deps/ctypesgen/src/ctypesgen/parser/parsetab.py:20:1

    line length 16909 characters (raw pass, confidence 0.65)

    _lr_goto_items = {'translation_unit':([0,],[1,]),'external_declaration':([1,],[2,]),'directive':([1,],[3,]),'declaration':([1,11,23,58,61,84,96,],[4,60,60,93,60,93,93,]),'function_definition':([1,],[5,]),'define':([1,],[6,]),'undefine':([1,],[7,]),'pragma':([1,],[8,]),'declaration_impl':([1,11,23,58,61,84,96,],[9,9,9,9
    likely benign LLM, confidence 0.92 Generated PLY goto table with a long data line.
    Reasoning

    Same generated parse table: a long dictionary literal of grammar goto entries, which is normal for this file type.

  8. info warn → info by review decoder-import-with-exec deps/ctypesgen/src/ctypesgen/parser/yacc.py:1568:25

    file imports decoder module(s) cPickle, pickle and calls `exec`/`eval`/`compile` — multi-stage payload shape (ast pass, confidence 0.80)

                    try:
                        c = eval(s)
                        if (len(c) > 1):
                            raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
    likely benign LLM, confidence 0.80 Vendored PLY evaluates a literal token string from the grammar specification.
    Reasoning

    PLY's grammar parser calls eval on a grammar-declared literal token string to check it is a single character. The input comes from the grammar author, and the pickle import is incidental.

  9. info warn → info by review dynamic-execution deps/ctypesgen/src/ctypesgen/parser/yacc.py:1568:25

    `eval` called on a non-literal expression (ast pass, confidence 0.70)

                    try:
                        c = eval(s)
                        if (len(c) > 1):
                            raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
    likely benign LLM, confidence 0.80 Duplicate flag of the PLY literal-token eval call.
    Reasoning

    This is the same eval(s) call on a grammar literal flagged a second time by the dynamic-execution signal. It is the PLY literal-token check, not a payload.

  10. info warn → info by review dynamic-execution deps/ctypesgen/src/ctypesgen/parser/yacc.py:1988:13

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            else:
                exec('import %s' % module)
                parsetab = sys.modules[module]
    likely benign LLM, confidence 0.85 Vendored PLY parser imports its parsetab module via exec.
    Reasoning

    PLY's yacc loads its parsetab module by name via exec('import %s' % module). This is the standard library-internal import pattern, not untrusted execution.

  11. info critical → info by review frame-introspection deps/ctypesgen/src/ctypesgen/parser/yacc.py:2890:9

    `sys._getframe` — call-stack frame introspection — file also calls `sys.exit`/`exec`/`eval` (anti-analysis shape) (ast pass, confidence 0.85)

    def get_caller_module_dict(levels):
        f = sys._getframe(levels)
        ldict = f.f_globals.copy()
        if f.f_globals != f.f_locals:
    likely benign LLM, confidence 0.90 Vendored PLY helper uses sys._getframe to read caller namespaces.
    Reasoning

    This is the standard PLY get_caller_module_dict helper using sys._getframe to access the caller's globals. The anti-analysis label is a false match on incidental sys.exit use.

  12. info warn → info by review dynamic-execution deps/ctypesgen/src/ctypesgen/parser/yacc.py:3264:17

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

                    pkgname = '.'.join(parts[:-1])
                    exec('import %s' % pkgname)
                    srcfile = getattr(sys.modules[pkgname], '__file__', '')
            outputdir = os.path.dirname(srcfile)
    likely benign LLM, confidence 0.85 Vendored PLY code imports a package by computed name to locate its output directory.
    Reasoning

    Same PLY pattern: exec('import %s' % pkgname) to resolve a package's directory for output. The name is computed from the library's own package layout.

Reproduce This Scan

Run disclude locally to verify. These commands scan the published file; the "new in this release" marks come from comparing it with the previous release's file, which they don't repeat. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.3.0
curl -fsSL -o pypdfium2-5.15.0.tar.gz https://files.pythonhosted.org/packages/7c/7d/c1761532c87e2da867a1049bf9142a17efec0b00c6326a51964bec2388ea/pypdfium2-5.15.0.tar.gz
printf '%s  %s\n' 150f71bc385fe66d82fc59514b31d00dcfc97a1143d6b4cb09aa6f532f3f3510 pypdfium2-5.15.0.tar.gz | shasum -a 256 -c -
mkdir pkg && tar -xzf pypdfium2-5.15.0.tar.gz -C pkg --strip-components 1
disclude scan --untrusted --severity info pkg