PyPI / reportlab / 5.1.0

reportlab 5.1.0

Artifact scanned
reportlab-5.1.0-py3-none-any.whl (wheel · 1.9 MB · SHA-256 59fdbaec5efe…)
Compared with
reportlab-5.0.1-py3-none-any.whl (8 signals introduced since)
Files
166 scanned, 48 skipped (binary, oversized, or unrecognised)
Signals
1 critical · 5 warn · 45 info
Scan time
43.6 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 50 of 50 warn and critical signals; 44 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-10-23 11:15

Signals

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

  1. critical builtins-write reportlab/lib/utils.py:143:9

    `setattr(builtins, ...)` — patches a built-in for every importer of this module (ast pass, confidence 0.95)

        for k,v in kwd.items():
            setattr(builtins,k,v)
    
    def zipImported(ldr=None):
    likely benign LLM, confidence 0.60 Explicit helper that assigns the given names onto the builtins module.
    Reasoning

    Setting arbitrary builtins from keyword arguments is a global side effect that affects every importer, which is worth noting. It is an explicit, caller-invoked compatibility helper, not hidden or triggered on import, so the risk is design-level rather than malicious.

  2. warn dynamic-execution reportlab/lib/utils.py:846:16

    `marshal.loads` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

                import marshal
                md=marshal.loads(__rl_loader__.get_data('meta_data.mar'))
                project_version=md['project_version']
            except:
    likely benign LLM, confidence 0.65 Loads bundled package metadata (project version) from a marshal file.
    Reasoning

    marshal.loads on a file read from the package's own loader is a deserialization sink in principle, but the data is a bundled metadata file used only to read a version string. Nothing here indicates attacker-supplied input, though the marshal format does deserve a check.

  3. warn dynamic-execution reportlab/pdfbase/cidfonts.py:205:29

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

            f = open(os.path.join(directory, self.name + '.fastmap'), 'rb')
            self._mapFileHash = marshal.load(f)
            self._codeSpaceRanges = marshal.load(f)
            self._notDefRanges = marshal.load(f)
    likely benign LLM, confidence 0.60 marshal.load of a local .fastmap CMap cache file.
    Reasoning

    marshal.load can in principle yield code objects, which makes it a sensitive sink. Here it reads reportlab's own .fastmap cache file from a local font directory, which is a shipped data artifact. The risk depends on whether that directory can be written by an attacker, which the snippet does not show.

  4. warn dynamic-execution reportlab/pdfbase/cidfonts.py:206:33

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

            self._mapFileHash = marshal.load(f)
            self._codeSpaceRanges = marshal.load(f)
            self._notDefRanges = marshal.load(f)
            self._cmap = marshal.load(f)
    likely benign LLM, confidence 0.60 marshal.load reading a cached CMap field from a local file.
    Reasoning

    Same marshal.load of consecutive fields from the local .fastmap cache, with no network or external input in the snippet. It is a benign cache-loading pattern, with the same residual marshal risk.

  5. warn dynamic-execution reportlab/pdfbase/cidfonts.py:207:30

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

            self._codeSpaceRanges = marshal.load(f)
            self._notDefRanges = marshal.load(f)
            self._cmap = marshal.load(f)
            f.close()
    likely benign LLM, confidence 0.60 marshal.load reading another cache field from the local .fastmap file.
    Reasoning

    Same cache-file read pattern: marshal.load of a field from reportlab's own .fastmap, followed by f.close(). Benign in context, with the usual caveat about marshal.

  6. warn dynamic-execution reportlab/pdfbase/cidfonts.py:208:22

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

            self._notDefRanges = marshal.load(f)
            self._cmap = marshal.load(f)
            f.close()
            finished = time.clock()
    likely benign LLM, confidence 0.60 marshal.load reading the CMap from the local .fastmap cache.
    Reasoning

    Final marshal.load in the cache-reading sequence, reading the cmap from a local file and then closing it. This is the usual cache-loading behaviour of this font module.

  7. info critical → info by review frame-introspection reportlab/lib/rl_safe_eval.py:928:13 new in this release

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

        if g is None:
            g = sys._getframe(1).f_globals.copy()
        for name in ('__annotations__', '__doc__', '__loader__', '__name__', '__package__', '__spec__'):
            if name in g:
    likely benign LLM, confidence 0.85 Captures the caller's global namespace to build a safe evaluation environment.
    Reasoning

    Copying the caller's globals via sys._getframe to seed a restricted evaluation namespace is a normal pattern in a safe-eval module. The anti-analysis label is a heuristic artifact of the file also calling eval.

  8. info warn → info by review dynamic-execution reportlab/lib/rl_safe_eval.py:1283:21 new in this release

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

            if not visit:
                bcode = compile(src, fname, mode=mode, flags=flags, dont_inherit=not inherit)
            else:
                astc = ast.parse(src, fname, mode)
    likely benign LLM, confidence 0.80 Compiles an expression string inside the safe-eval implementation.
    Reasoning

    This file is a restricted-expression evaluator whose purpose is to compile user expressions; compile is its core function and is gated by an AST whitelist elsewhere in the module.

  9. info warn → info by review dynamic-execution reportlab/lib/rl_safe_eval.py:1291:21 new in this release

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

                    print('post:\n%s\n'%astFormat(astc))
                bcode = compile(astc, fname, mode=mode)
            return bcode, names_seen
    likely benign LLM, confidence 0.80 Compiles a previously parsed and vetted AST inside the safe-eval pipeline.
    Reasoning

    Compiling a checked AST in the safe-eval module is the expected pipeline stage. The non-literal input is the intended use of the library.

  10. info critical → info by review frame-introspection reportlab/lib/rl_safe_eval.py:1304:21 new in this release

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

            if None in (l,g):
                frame = sys._getframe(__frame_depth__)
                G = frame.f_globals
                mname = G.get('__name__',None)
    likely benign LLM, confidence 0.85 Reads the caller frame's globals to determine evaluation context.
    Reasoning

    Reading the calling frame's globals to resolve the module name for the evaluation context is routine. The anti-analysis tag only reflects that the file contains eval.

  11. info warn → info by review dynamic-execution reportlab/lib/rl_safe_eval.py:1336:20 new in this release

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

            try:
                return eval(bcode,G,L)
            finally:
                sbi = sbi.__self__
    likely benign LLM, confidence 0.80 Evaluates compiled expression bytecode in the restricted safe-eval environment.
    Reasoning

    This eval runs bytecode that was produced by the module's own restricted compile path, with a sandboxed builtins mapping; this is the library's designed purpose rather than an injection sink.

  12. info critical → info by review frame-introspection reportlab/lib/rl_safe_eval.py:1355:16 new in this release

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

                  allowed_magic_names=None, mnames=(), excluded_names=None):
            _loc = inspect.stack()[1]
            #print(f'+++++ {os.path.basename(_loc[1])}:{_loc[2]} {expr=}')
            if not self.env: self.env = __RL_SAFE_ENV__()
    likely benign LLM, confidence 0.90 Records the calling location via inspect.stack for the safe-eval environment.
    Reasoning

    inspect.stack() is used only to record the caller location for the environment, with the debugging print commented out. This is ordinary diagnostic code.

  13. info warn → info by review dynamic-execution reportlab/lib/rl_safe_eval.py:1431:12 new in this release

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

            rl_safe_eval('""')
        return eval(expr,__rl_safe_builtins__,NS)
    
    class DefaultNamespace:
    likely benign LLM, confidence 0.80 Evaluates an expression with restricted builtins in a constructed namespace.
    Reasoning

    The eval uses restricted builtins and a constructed namespace inside the safe-eval helper, which is the module's documented function. The risk is inherent to a sandbox and the file shows its restrictions.

  14. info warn → info by review dynamic-attribute reportlab/pdfbase/pdfmetrics.py:38:17 new in this release

    reach-by-name through `globals` (ast pass, confidence 0.60)

            for mn in '_typefaces _fonts'.split():
                m = globals()[mn]
                if fn in m:
                    del m[fn]
    likely benign LLM, confidence 0.94 Clears a font or typeface registry entry by looking up module-level cache dictionaries by name.
    Reasoning

    The code iterates over the names '_typefaces' and '_fonts' and deletes entries from the module's own caches via globals(). This is ordinary registry cache management in a PDF library, and the dynamic lookup only touches fixed internal names.

  15. info warn → info by review encoding-octal reportlab/graphics/barcode/code39.py:93:6

    16 consecutive `\NNN` octal escapes (entropy 4.0 bits/byte) (raw pass, confidence 0.65)

    _extchrs = _stdchrs + ascii_lowercase + \
        "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" + \
        "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" + \
        "*!'#&\"(),:;<=>?@[\\]^_`{|}~\177"
    likely benign LLM, confidence 0.90 Extended Code 39 character table written with octal escapes.
    Reasoning

    The octal escapes are the ASCII control-character table for the Code 39 extended character set, a fixed lookup string in a barcode encoder. Sequential octal escapes are the normal way to write that table.

  16. info warn → info by review encoding-octal reportlab/graphics/barcode/code39.py:94:6

    16 consecutive `\NNN` octal escapes (entropy 4.0 bits/byte) (raw pass, confidence 0.65)

        "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" + \
        "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" + \
        "*!'#&\"(),:;<=>?@[\\]^_`{|}~\177"
    likely benign LLM, confidence 0.90 Continuation of the Code 39 extended character table.
    Reasoning

    Continuation of the same ASCII lookup table in the Code 39 extended character set. Nothing is decoded or executed.

  17. info warn → info by review long-line reportlab/graphics/charts/lineplots.py:901:1

    line length 4983 characters (raw pass, confidence 0.65)

            self.data=[(20030601,0.95,0.05,0.0),(20030701,0.95,0.05,0.0),(20030801,0.95,0.05,0.0),(20030901,0.95,0.05,0.0),(20031001,0.95,0.05,0.0),(20031101,0.95,0.05,0.0),(20031201,0.95,0.05,0.0),(20040101,0.95,0.05,0.0),(20040201,0.95,0.05,0.0),(20040301,0.95,0.05,0.0),(20040401,0.95,0.05,0.0),(20040501,0.95,0.05,0.0),(
    likely benign LLM, confidence 0.90 Long inline chart data literal in a sample.
    Reasoning

    A long line holding a literal list of date and float tuples for a chart demo sample. Long data literals are a formatting issue, not obfuscation.

  18. info warn → info by review dynamic-attribute reportlab/graphics/charts/piecharts.py:1703:19

    reach-by-name through `globals` (ast pass, confidence 0.60)

            name = a if a.startswith('sample') else 'sample%s' % a
            drawing = globals()[name]()
            renderPDF.drawToFile(drawing, '%s.pdf' % name)
    likely benign LLM, confidence 0.80 Demo runner looks up sample drawing functions by name via globals.
    Reasoning

    The name is a sample identifier from the command line or a fixed list, resolved to a module-level sample function in a demo main block. Reaching functions by name is a common, harmless pattern here.

  19. info critical → info by review dynamic-import reportlab/graphics/samples/runall.py:16:26

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

    def getclass(f):
        return moduleClasses(__import__(f))
    
    def run(format, VERBOSE=0):
    likely benign LLM, confidence 0.75 Sample runner imports sample modules dynamically by name.
    Reasoning

    __import__ receives a module name from the sample runner's own loop over known sample modules, so it is a plugin-style loader in a test/demo script. Non-literal import is flagged, but there is no sign of attacker-controlled input or a payload.

  20. info warn → info by review encoding-escape-soup reportlab/graphics/testshapes.py:442:121

    27 consecutive `\xNN` escapes (entropy 4.3 bits/byte) (raw pass, confidence 0.80)

                y -= th
                text = fontName+asNative(b': I should be totally horizontal and enclosed in a box and end in alphabetagamma \xc2\xa2\xc2\xa9\xc2\xae\xc2\xa3\xca\xa5\xd0\x96\xd6\x83\xd7\x90\xd9\x82\xe0\xa6\x95\xce\xb1\xce\xb2\xce\xb3')
                textWidth = stringWidth(text, fontName, fontSize)
    likely benign LLM, confidence 0.90 UTF-8 byte escapes as internationalized test text.
    Reasoning

    A long string of UTF-8 byte escapes used as multilingual rendering test text in a test-shapes module. Expected for font and encoding tests, and it does not decode to anything executable.

  21. info identifier-low-length reportlab/graphics/transform.py:1:1

    91 of 224 non-conventional identifiers are single-character (41%) (token pass, confidence 0.50)

  22. info warn → info by review payload-bytes-literal reportlab/lib/arciv.py:93:12

    bytes literal contains 512 `\xNN` escapes (95% of content) — binary payload shape (ast pass, confidence 0.85)

    		'key': b"\x01\x23\x45\x67\x89\xab\xcd\xef",
    		'input': b"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
    \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
    \x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\
    likely benign LLM, confidence 0.85 Cipher test vector with key, input and output bytes.
    Reasoning

    The bytes literal is a key, plaintext input and expected output test vector for a cipher self-test. Binary-looking literals are normal in cryptographic test data and are not decoded or executed.

  23. info warn → info by review payload-bytes-literal reportlab/lib/arciv.py:145:12

    bytes literal contains 512 `\xNN` escapes (95% of content) — binary payload shape (ast pass, confidence 0.85)

    \x01",
    	'output': b"\x75\x95\xc3\xe6\x11\x4a\x09\x78\x0c\x4a\xd4\
    \x52\x33\x8e\x1f\xfd\x9a\x1b\xe9\x49\x8f\
    \x81\x3d\x76\x53\x34\x49\xb6\x77\x8d\xca\
    likely benign LLM, confidence 0.85 Expected ciphertext bytes in a cipher self-test.
    Reasoning

    Continuation of cipher test vectors: the output bytes are the expected ciphertext. This is standard self-test data in a crypto module.

  24. info warn → info by review dynamic-attribute reportlab/lib/colors.py:1018:9

    reach-by-name through `globals` (ast pass, confidence 0.60)

        for k, c in assigned.items():
            globals()[k] = c
            if isinstance(c,Color): _namedColors[k] = c
    likely benign LLM, confidence 0.85 Populates module globals and named colour registry from a dict.
    Reasoning

    The loop copies named colour constants from a local dict into module globals and into the named-colour registry. It is a known pattern for populating module namespaces and does not pull in external code.

  25. info warn → info by review payload-bytes-literal reportlab/lib/pdfencrypt.py:47:20

    bytes literal contains 159 `\xNN` escapes (86% of content) — binary payload shape (ast pass, confidence 0.85)

        _os_random_b = b'\xbd\x8f\xdc\xabovp\xe8\x15\xec\\C\x9d\x92B~\xb8\xf4\xdeEg8\xb2f\x80Sj\'y\xcfG\xcaY\xb9\xdc-\xc4Q\x17\x88\xaf\xd1\xf7\x7f\xa1L>\x99\x89i\xf7\xc4\xb4\'\xe9k\xc9\xfa\xa6p\x80\xcd\xaa\xaf|\x97\xf7\xcc \xc1\xef\xc7\x97\xd2;\xaf\xe1\xfc\x16,\xd3\x0b\x19\xa1\x02\xe6\x01\xcb\x1c\xd8\xe6\\H}\r\xdc\x85\xe1\
    likely benign LLM, confidence 0.90 Standard PDF encryption padding constant as bytes.
    Reasoning

    The bytes are the standard 32-byte PDF encryption padding string, a fixed constant defined by the PDF specification. Its shape is expected for this purpose.

  26. info warn → info by review decoder-import-with-exec reportlab/lib/pdfencrypt.py:723:29

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

                            if thisarg[0] in binaryrequired:
                                exec(thisarg[1] +' = int(argv[pos+1])',vars())
                            else:
                                exec(thisarg[1] +' = argv[pos+1]',vars())
    likely benign LLM, confidence 0.75 Command-line option parsing assigns values via exec on constructed names.
    Reasoning

    The exec statements build assignments from a hardcoded table of option names and assign command-line values to them. This is poor practice and a code smell, but the target names come from a fixed table, not from attacker-controlled code, and there is no network or process activity. The decoder import alongside exec matches the module's own encryption helpers rather than a dropper.

  27. info critical → info by review dynamic-execution reportlab/lib/pdfencrypt.py:723:29

    `exec` called on a constructed string (ast pass, confidence 0.80)

                            if thisarg[0] in binaryrequired:
                                exec(thisarg[1] +' = int(argv[pos+1])',vars())
                            else:
                                exec(thisarg[1] +' = argv[pos+1]',vars())
    likely benign LLM, confidence 0.70 Exec of a constructed assignment for a command-line option.
    Reasoning

    Same exec on a constructed assignment string as the previous finding. It is a dynamic-execution smell, but the string is built from a fixed option-name table with a command-line value, so this is most likely benign code.

  28. info critical → info by review dynamic-execution reportlab/lib/pdfencrypt.py:725:29

    `exec` called on a constructed string (ast pass, confidence 0.80)

                            else:
                                exec(thisarg[1] +' = argv[pos+1]',vars())
                            if verbose:
                                print("%s set to: '%s'." % (thisarg[3], argv[pos+1]))
    likely benign LLM, confidence 0.70 String-option branch of the same exec-based argument parsing.
    Reasoning

    Second exec branch of the same option-parsing loop, assigning a string option value from argv through a fixed name table. Unsafe-looking but not evidence of malicious intent.

  29. info warn → info by review dynamic-attribute reportlab/lib/randomtext.py:325:21

    reach-by-name through `globals` (ast pass, confidence 0.60)

            elif theme.upper() in ('STARTUP','COMPUTERS','BLAH','BUZZWORD','STARTREK','PRINTING','PYTHON'):
                theme = globals()[theme.upper()]
            else:
                raise ValueError('Unknown theme "%s"' % theme)
    likely benign LLM, confidence 0.90 Selects a random-text word list from module globals by theme name.
    Reasoning

    The globals() lookup indexes a fixed set of module-level word lists by an upper-cased theme name that is checked against a whitelist just above, so it selects data and runs no attacker-controlled code.

  30. info critical → info by review dynamic-execution reportlab/lib/rl_accel.py:26:9

    `exec` at module scope (runs on import) on a non-literal expression (ast pass, confidence 0.80)

        try:
            exec('from _rl_accel import %s as f' % fn,D)
            _c_funcs[fn] = D['f']
            if testing: _py_funcs[fn] = None
    likely benign LLM, confidence 0.85 Optionally imports compiled accelerator functions from _rl_accel via exec.
    Reasoning

    The exec runs a fixed import statement for names drawn from a hardcoded list of accelerator functions, at module scope. Critical severity is overstated; it is a standard optional C-accelerator loader.

  31. info critical → info by review frame-introspection reportlab/lib/testutils.py:127:8

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

    def printLocation(depth=1):
        if sys._getframe(depth).f_locals.get('__name__')=='__main__':
            outDir = outputfile('')
            if outDir!=_OUTDIR:
    likely benign LLM, confidence 0.90 Test helper that inspects the caller frame to decide output location.
    Reasoning

    A test helper reads the caller's frame locals to check whether it is running as __main__ and to pick an output directory. This is benign test scaffolding.

  32. info warn → info by review dynamic-execution reportlab/lib/testutils.py:188:20

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

            if self.pat.match(val):
                return eval(val,{__builtins__:None})
            else:
                return value
    likely benign LLM, confidence 0.80 Evaluates a pattern-matched literal value with builtins disabled.
    Reasoning

    The eval only runs on values that first matched a restrictive pattern, with __builtins__ set to None. It is a limited literal-parsing helper in test utilities.

  33. info warn → info by review frame-introspection reportlab/lib/utils.py:148:19

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

            if not ldr:
                ldr = sys._getframe(1).f_globals['__loader__']
            from zipimport import zipimporter
            return ldr if isinstance(ldr,zipimporter) and len(ldr._files) else None
    likely benign LLM, confidence 0.90 Detects whether the package was loaded from a zip archive via the caller's loader.
    Reasoning

    Reading the caller's __loader__ to detect a zipimport-based installation is a standard, benign packaging check.

  34. info critical → info by review dynamic-import reportlab/lib/utils.py:967:17

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

                try:
                    __import__(k)
                    m = sys.modules[k]
                    d = getattr(m,'__version__',None)==vk0 and 'SAME' or 'DIFFERENT'
    likely benign LLM, confidence 0.85 Dynamic __import__ of a library name inside a version-check loop.
    Reasoning

    The snippet is a try/except loop that imports a module by name, reads its __version__ and compares it to an expected value. Checking installed library versions this way is a common, legitimate pattern. The argument is a loop variable over known library names, not attacker-controlled input.

  35. info critical → info by review dynamic-import reportlab/lib/utils.py:1004:17

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

                    A = [mn].append
                    __import__(mn)
                    m = sys.modules[mn]
                    A(m.__file__)
    likely benign LLM, confidence 0.85 Dynamic __import__ of a module name to collect its file path.
    Reasoning

    The snippet imports a module by name from a list and appends its __file__ to a list. This is a module-location lookup, which is ordinary library behaviour. Nothing here decodes data or reaches the network.

  36. info warn → info by review frame-introspection reportlab/lib/utils.py:1054:18

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

        while 1:
            _ = func(sys._getframe(depth).f_locals)
            if _: return _
            depth += 1
    likely benign LLM, confidence 0.80 Call-stack walk via sys._getframe that hands frame locals to a callback.
    Reasoning

    A loop walks up the call stack with sys._getframe and passes each frame's locals to a callback, stopping on a truthy result. This is a known stack-inspection technique used by libraries for lookups. It is not a hidden payload, though it is a mild code smell.

  37. info warn → info by review long-line reportlab/pdfbase/_cidfontdata.py:464:1

    line length 2205 characters (raw pass, confidence 0.65)

    widthsByUnichar["HeiseiKakuGo-W5"] = {u'\uff81': 500, u'\uff85': 500, u'\uff89': 500, u'\uff8d': 500, u'\uff91': 500, u'\uff95': 500, u'\uff99': 500, u'\uff9d': 500, u' ': 277, u'$': 668, u'(': 445, u',': 305, u'0': 668, u'\u0332': 668, u'4': 668, u'8': 668, u'<': 668, u'@': 871, u'D': 699, u'H': 687, u'L': 582, u'P': 
    likely benign LLM, confidence 0.95 Very long generated data line holding glyph width mappings.
    Reasoning

    This is a generated width table for CJK fonts stored as a single very long Python dict literal. Long lines are typical of generated data files, and the content is plain numeric mappings.

  38. info warn → info by review long-line reportlab/pdfbase/_cidfontdata.py:467:1

    line length 3962 characters (raw pass, confidence 0.65)

    widthsByUnichar["HeiseiMin-W3"] = {u'\uff81': 500, u'\u0302': 333, u'\uff85': 500, u'\u0306': 333, u'\uff89': 500, u'\u030a': 333, u'\uff8d': 500, u'\uff91': 500, u'\ufb02': 556, u'\uff95': 500, u'\uff99': 500, u'\uff9d': 500, u' ': 250, u'\xa3': 500, u'\u2122': 980, u'$': 500, u'(': 333, u'\xab': 500, u',': 250, u'\xa
    likely benign LLM, confidence 0.95 Very long generated data line of glyph widths for a CJK font.
    Reasoning

    Same generated width table as the other long-line finding, a plain dict literal of character widths for a Japanese font. Data-file line length alone is not suspicious.

  39. info warn → info by review encoding-octal reportlab/pdfbase/_cidfontdata.py:472:14

    19 consecutive `\NNN` octal escapes (entropy 3.6 bits/byte) (raw pass, confidence 0.65)

    #shift-jis saying 'This is Heisei-Minchou'
    message1 =  '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
    message2 = '\202\261\202\352\202\315\225\275\220\254\212p\203S\203V\203b\203N\202\305\202\267\201B'
    likely benign LLM, confidence 0.93 Shift-JIS sample string written as octal byte escapes, with a comment explaining it.
    Reasoning

    The octal escapes encode Shift-JIS bytes of a sample Japanese string, and the comment explains the purpose. This is test or example text for CJK rendering, not obfuscated code.

  40. info warn → info by review encoding-octal reportlab/pdfbase/_cidfontdata.py:473:13

    11 consecutive `\NNN` octal escapes (entropy 3.0 bits/byte) (raw pass, confidence 0.65)

    message1 =  '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
    message2 = '\202\261\202\352\202\315\225\275\220\254\212p\203S\203V\203b\203N\202\305\202\267\201B'
    
    ##def pswidths(text):
    likely benign LLM, confidence 0.93 Shift-JIS sample message as octal escapes in an example.
    Reasoning

    This is the same documented Shift-JIS sample text as the neighbouring finding, shown with a commented-out helper. It is a literal encoding example with no execution path.

  41. info warn → info by review dynamic-attribute reportlab/pdfbase/_fontdata.py:268:11

    reach-by-name through `globals` (ast pass, confidence 0.60)

        for k,v in initial_dicts.items():
            d=globals()[k]
            d.clear()
            d.update(v)
    likely benign LLM, confidence 0.80 Resets module-level font dictionaries by name via globals().
    Reasoning

    The loop resets module-level dictionaries by name through globals() using keys from a fixed initial_dicts mapping. This is a normal way to restore font metadata state and does not run attacker-supplied code.

  42. info warn → info by review encoding-octal reportlab/pdfbase/cidfonts.py:473:17

    19 consecutive `\NNN` octal escapes (entropy 3.6 bits/byte) (raw pass, confidence 0.65)

        # system, print the string to see Kanji
        message1 = '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
        c.drawString(100, 675, message1)
        c.save()
    likely benign LLM, confidence 0.93 Shift-JIS demo string as octal escapes in a drawString example.
    Reasoning

    The octal escapes are the Shift-JIS bytes for a sample string used in a PDF demo, with a comment telling the user to view it in a Japanese locale. This is documentation-style example text, not a payload.

  43. info warn → info by review decoder-decompress-payload reportlab/pdfbase/pdfdoc.py:754:16

    file imports decoder module(s) binascii, codecs, zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

        def decode(self, encoded):
            return zlib.decompress(encoded)
    
    # need only one of these, unless we implement parameters later
    likely benign LLM, confidence 0.93 A PDF FlateDecode helper that calls zlib.decompress on stream data.
    Reasoning

    This is a generic decode method wrapping zlib.decompress, the standard FlateDecode filter used when reading PDF streams. Decompressing data is the library's job here, and the snippet shows no hidden payload, execution, or network access.

  44. info warn → info by review dynamic-attribute reportlab/pdfbase/pdfmetrics.py:892:11

    reach-by-name through `globals` (ast pass, confidence 0.60)

        for k,v in initial_dicts.items():
            d=globals()[k]
            d.clear()
            d.update(v)
    likely benign LLM, confidence 0.94 Resets module-level registry dictionaries from a saved snapshot.
    Reasoning

    The loop restores known module-level dictionaries from a fixed initial_dicts mapping by clearing and updating them. It is a state reset with no external input or execution, which is typical of test or reset helpers.

  45. info warn → info by review dynamic-attribute reportlab/pdfgen/canvas.py:1039:34

    reach-by-name through `vars` (ast pass, confidence 0.60)

                for k in extraReturn.keys():
                    extraReturn[k] = vars()[k]
    
            return (imgObj.width, imgObj.height)
    likely benign LLM, confidence 0.93 Copies named local variables from an image-handling function into a return dictionary.
    Reasoning

    vars() is used to copy named local variables into an extraReturn dict for keys the caller requested. This is a local-variable readback pattern that does not hide intent or reach external resources.

  46. info warn → info by review dynamic-attribute reportlab/pdfgen/pdfimages.py:186:34

    reach-by-name through `vars` (ast pass, confidence 0.60)

                for k in extraReturn.keys():
                    extraReturn[k] = vars()[k]
            return True
    likely benign LLM, confidence 0.93 Copies requested local variables into an extraReturn dictionary during image processing.
    Reasoning

    This is the same vars()-based readback of requested local values as in canvas.py, used to populate an extraReturn dict. It is ordinary library code.

  47. info warn → info by review frame-introspection reportlab/platypus/tables.py:1286:14

    `sys._getframe` — call-stack frame introspection (ast pass, confidence 0.70)

            import sys
            op = sys._getframe(1).f_locals['op']
            raise ValueError(f'Unknown line command {op!a}')
    likely benign LLM, confidence 0.85 Reads the caller's 'op' local variable to include it in an unknown-line-command error message.
    Reasoning

    sys._getframe(1) reads the caller's 'op' local to build an error message for an unknown line command. It is unusual and introspective, but it only feeds a ValueError message and has no execution, process, or network behaviour.

  48. info warn → info by review dynamic-attribute reportlab/rl_config.py:71:5

    reach-by-name through `globals` (ast pass, confidence 0.60)

        if chk: chk(name,value)
        globals()[name] = value
    
    def _startUp():
    likely benign LLM, confidence 0.93 Stores a configuration option as a module-level global, optionally after validation.
    Reasoning

    The _setOpt helper stores a configuration value under a name in globals() after an optional check callback. This is the standard way a configuration module exposes its settings, and the name comes from the library's own option set.

  49. info warn → info by review dynamic-attribute reportlab/rl_config.py:91:25

    reach-by-name through `globals` (ast pass, confidence 0.60)

            for k,v in _DEFAULTS.items():
                _SAVED[k] = globals()[k] = v
                if k not in __all__:
                    A.append(k)
    likely benign LLM, confidence 0.93 Initialises module-level configuration globals from a defaults dictionary during startup.
    Reasoning

    The loop writes default values from _DEFAULTS into module globals and records them in _SAVED, and it appends names to __all__. This is normal startup initialisation of configuration defaults.

  50. info warn → info by review dynamic-attribute reportlab/rl_config.py:126:13

    reach-by-name through `globals` (ast pass, confidence 0.60)

                _setOpt(k,os.pathsep.join(P),lambda x:x.split(os.pathsep))
                globals()[k] = list(filter(rl_isdir,globals()[k]))
            else:
                v = _SAVED[k]
    likely benign LLM, confidence 0.93 Filters a configured list of search directories down to those that exist.
    Reasoning

    The code rewrites a path-list configuration global by filtering it with rl_isdir, which keeps only existing directories. This is configuration normalisation with no external execution or payload.

  51. info warn → info by review dynamic-attribute reportlab/rl_config.py:126:49

    reach-by-name through `globals` (ast pass, confidence 0.60)

                _setOpt(k,os.pathsep.join(P),lambda x:x.split(os.pathsep))
                globals()[k] = list(filter(rl_isdir,globals()[k]))
            else:
                v = _SAVED[k]
    likely benign LLM, confidence 0.93 Reads the configured directory list to filter it to existing directories.
    Reasoning

    This is the read side of the same directory-list filtering on the configuration global, a finding that is part of the same benign configuration logic.

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 reportlab-5.1.0-py3-none-any.whl https://files.pythonhosted.org/packages/4a/3c/bf68d44f160b44685fc714f746a954c71d0091b3dc4fd3c8670154b44841/reportlab-5.1.0-py3-none-any.whl
printf '%s  %s\n' 59fdbaec5efe437095ad1d7292ec9a271c8ca2d3ec75e889c6e61400929b90df reportlab-5.1.0-py3-none-any.whl | shasum -a 256 -c -
unzip -q reportlab-5.1.0-py3-none-any.whl -d pkg
disclude scan --untrusted --severity info pkg