PyPI / pypdf / 6.21.2

pypdf 6.21.2

Source
https://github.com/py-pdf/pypdf
Tag scanned
6.21.2
Compared with
6.21.1 (0 signals introduced since)
Files
187 scanned, 110 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 40 info
Scan time
18.3 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 28 of 29 warn and critical signals; 29 demoted to info.
Disclude version
4.1.0
Scanned (UTC)
2026-10-22 09:14

Signals

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

  1. info warn → info by review encoding-base64 docs/meta/faq.md:21:288

    base64-like blob (66 bytes, compression ratio 1.17) (raw pass, confidence 0.60)

    ://github.com/Giacomo-De-Florio-Dev/Make_Your_PDF_Safe/tree/ec439f92243d12d54ae024668792470c6b40ee96/MakeYourPDFsafe_V1.3/PyPDF2). That
    dismissed LLM, confidence 0.90 A GitHub URL path in a markdown FAQ was flagged as a base64-like blob.
    Reasoning

    The base64-like match is a fragment of a GitHub URL path containing a commit hash and directory names, not an encoded payload. Raw pass has high false-positive rate.

  2. info warn → info by review payload-bytes-literal pypdf/_encryption.py:117:5

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

    _PADDING = (
        b"\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
        b"\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"
    )
    likely benign LLM, confidence 0.85 The PDF standard padding constant defined as a bytes literal in the encryption module.
    Reasoning

    The bytes literal is the standard PDF encryption padding string (the 32-byte PDF spec padding constant used in RC4/AES key derivation). Binary-looking escapes are expected for this legitimate cryptographic constant in a PDF library.

  3. info warn → info by review encoding-hex pypdf/_encryption.py:117:7

    16 consecutive `\xNN` escapes (entropy 3.7 bits/byte) (raw pass, confidence 0.65)

    _PADDING = (
        b"\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
        b"\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"
    )
    likely benign LLM, confidence 0.85 Hex-escaped PDF padding bytes in the encryption module.
    Reasoning

    Hex escapes form the well-known PDF padding constant; this is a fixed spec value, not obfuscated code.

  4. info warn → info by review encoding-hex pypdf/_encryption.py:118:7

    16 consecutive `\xNN` escapes (entropy 3.9 bits/byte) (raw pass, confidence 0.65)

        b"\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08"
        b"\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"
    )
    likely benign LLM, confidence 0.85 Continuation of the PDF padding constant bytes literal.
    Reasoning

    Continuation line of the same standard PDF padding constant; legitimate spec data.

  5. info warn → info by review dynamic-attribute pypdf/_page.py:2278:20

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

                ):
                    if locals()[visitor]:
                        logger_warning(
                            "Argument %(visitor)s is ignored in layout mode",
    likely benign LLM, confidence 0.75 Use of locals() to test whether a visitor argument is set in a text extraction function.
    Reasoning

    locals() is used to check whether a keyword argument (visitor) was passed, a common idiom for detecting ignored parameters in layout mode. Not a reach-by-name attack, though it is unusual style.

  6. info warn → info by review decoder-decompress-payload pypdf/filters.py:95:20

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

    def _decompress_with_limit(data: bytes) -> bytes:
        decompressor = zlib.decompressobj()
        configuration = get_configuration()
        result = decompressor.decompress(data, max_length=configuration.zlib_maximum_output_length)
    likely benign LLM, confidence 0.85 Size-limited zlib decompression used for PDF Flate filter data.
    Reasoning

    zlib decompressobj with max_length is the standard size-limited FlateDecode implementation for PDF streams. Decompressing data is the library's core purpose, not a hidden payload.

  7. info warn → info by review payload-bytes-literal pypdf/generic/_image_inline.py:195:13

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

            elif c in (
                b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc9\xca\xcb\xcc\xcd\xce\xcf"
                b"\xda\xdb\xdc\xdd\xde\xdf"
                b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xfe"
    likely benign LLM, confidence 0.85 Table of byte constants used when parsing inline image data.
    Reasoning

    The bytes literal lists JPEG/marker-like byte values used in inline image filter parsing (checking byte codes against a set). This is a legitimate constant table for PDF image parsing.

  8. info warn → info by review encoding-hex pypdf/generic/_image_inline.py:197:15

    17 consecutive `\xNN` escapes (entropy 4.1 bits/byte) (raw pass, confidence 0.65)

                b"\xda\xdb\xdc\xdd\xde\xdf"
                b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xfe"
            ):
                c = read(2)
    likely benign LLM, confidence 0.85 Continuation of a byte constant table in inline image parsing.
    Reasoning

    Continuation of the same legitimate byte-constant table in the inline image parser.

  9. info warn → info by review unicode-mixed-script resources/crazyones.txt:3:30

    identifier `misfits` mixes scripts: Latin + Other (raw pass, confidence 0.80)

    October 14, 1998
    Heres to the crazy ones. The misfits. The rebels. The troublemakers.
    The round pegs in the square holes.
    The ones who see things differently. Theyre not fond of rules. And
    dismissed LLM, confidence 0.95 Typographic ligature in a quote text test fixture.
    Reasoning

    The 'fi' ligature (U+FB01) in a text fixture is a typographic ligature from the source quote, a test resource for PDF text extraction. Not an identifier attack.

  10. info warn → info by review unicode-mixed-script resources/crazyones.txt:5:25

    identifier `differently` mixes scripts: Latin + Other (raw pass, confidence 0.80)

    The round pegs in the square holes.
    The ones who see things differently. Theyre not fond of rules. And
    they have no respect for the status quo. You can quote them,
    disagree with them, glorify or vilify them.
    dismissed LLM, confidence 0.95 Typographic ligature in a quote text fixture.
    Reasoning

    The 'ff' ligature (U+FB00) is typographic text in a plain text resource file, consistent with PDF extraction test content.

  11. info warn → info by review unicode-mixed-script resources/crazyones_layout_vertical_space.txt:4:34

    identifier `misfits` mixes scripts: Latin + Other (raw pass, confidence 0.80)

    
       Heres to the crazy ones.  The misfits.  The rebels.  The troublemakers.
           The round pegs in the square holes.
       The ones who see things differently.  Theyre not fond of rules.  And
    dismissed LLM, confidence 0.95 Ligature in a layout test fixture text.
    Reasoning

    Same typographic ligature in a text test fixture for layout extraction; benign.

  12. info warn → info by review unicode-mixed-script resources/crazyones_layout_vertical_space.txt:6:28

    identifier `differently` mixes scripts: Latin + Other (raw pass, confidence 0.80)

           The round pegs in the square holes.
       The ones who see things differently.  Theyre not fond of rules.  And
           they have no respect for the status quo.  You can quote them,
           disagree with them, glorify or vilify them.
    dismissed LLM, confidence 0.95 Ligature in a layout test fixture text.
    Reasoning

    Same typographic ligature in a layout test fixture; benign.

  13. info warn → info by review unicode-mixed-script resources/crazyones_layout_vertical_space_font_height_weight.txt:4:34

    identifier `misfits` mixes scripts: Latin + Other (raw pass, confidence 0.80)

    
       Heres to the crazy ones.  The misfits.  The rebels.  The troublemakers.
           The round pegs in the square holes.
    dismissed LLM, confidence 0.95 Ligature in a font/layout test fixture text.
    Reasoning

    Typographic ligature in a text test resource; no hidden or malicious intent.

  14. info warn → info by review unicode-mixed-script resources/crazyones_layout_vertical_space_font_height_weight.txt:7:28

    identifier `differently` mixes scripts: Latin + Other (raw pass, confidence 0.80)

    
       The ones who see things differently.  Theyre not fond of rules.  And
           they have no respect for the status quo.  You can quote them,
           disagree with them, glorify or vilify them.
    dismissed LLM, confidence 0.95 Ligature in a font/layout test fixture text.
    Reasoning

    Typographic ligature in a text test resource; benign.

  15. info warn → info by review long-line resources/jpeg.txt:1:1

    line length 82804 characters (raw pass, confidence 0.65)

    ffd8ffe000104a46494600010100000100010000ffdb0043000302020302020303030304030304050805050404050a070706080c0a0c0c0b0a0b0b0d0e12100d0e110e0b0b1016101113141515150c0f171816141812141514ffdb00430103040405040509050509140d0b0d1414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414ffc2
    likely benign LLM, confidence 0.95 A hex-encoded JPEG image dumped as a long text line in a resource file.
    Reasoning

    A long single line of hex-encoded JPEG bytes in a resource text file is a test fixture of binary data; no execution or hiding intent is shown.

  16. info warn → info by review unicode-mixed-script resources/multicolumn-lorem-ipsum.txt:8:52

    identifier `filled` mixes scripts: Other + Latin (raw pass, confidence 0.80)

    This  is  a  sample  document  with  two  columns  filled              molestie vitae, placerat a, molestie nec, leo.  Maece-
    with Lorem Ipsum text.                                                nas lacinia.  Nam ipsum ligula, eleifend at, accumsan
    dismissed LLM, confidence 0.93 A typographic fi-ligature in a lorem ipsum sample document.
    Reasoning

    The ligature character 'fi' in 'filled' is a normal typographic ligature in Lorem Ipsum sample text; a raw-pass unicode anomaly with no code execution context.

  17. info warn → info by review frame-introspection tests/generic/test_font.py:290:34

    `inspect.currentframe` — call-stack frame introspection (ast pass, confidence 0.70)

        def build_reversed_min(_self: table__c_m_a_p) -> Any:
            caller = cast(FrameType, inspect.currentframe()).f_back
            caller_name = cast(FrameType, caller).f_code.co_name
            # Check the backwards-compatible substitution works when calling from pypdf
    likely benign LLM, confidence 0.93 Test helper inspects its caller's frame name to check compatibility behavior.
    Reasoning

    Frame introspection in a test to verify a backward-compatible substitution called from pypdf is a legitimate testing technique in a PDF library.

  18. info unicode-homoglyph tests/test_cmap.py:197:18

    identifier `α` contains homoglyph candidates: α (03B1)→a (in string/comment) (raw pass, confidence 0.35)

        txt = reader.pages[0].extract_text()  # no error
        for pat in ("α", "β", "γ", "ϕ", "φ", "ℏ", "∫", "∂", "·", "×"):
            assert pat in txt
        # actually the ϕ and φ seems to be crossed in latex
  19. info warn → info by review payload-bytes-literal tests/test_filters.py:253:9

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

        assert CCITTFaxDecode.decode(data, parameters) == (
            b"II*\x00\x08\x00\x00\x00\x08\x00\x00\x01\x04\x00\x01\x00\x00\x00\x11\x00"
            b"\x00\x00\x01\x01\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x01"
            b"\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x03\x01\x03\x00\x01\x00"
    likely benign LLM, confidence 0.94 Binary CCITT fax image test data as a bytes literal.
    Reasoning

    A TIFF/CCITT binary test fixture expressed as bytes escapes in a PDF library test file; expected for image decoding tests.

  20. info warn → info by review payload-bytes-literal tests/test_filters.py:788:9

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

        image_data = (
            b'\x00\x00\x00\x010\x00\x01\x00\x00\x00\x13\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06"'
            b'\x00\x01\x00\x00\x00\x1c\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x9f\xa8_\xff\xac'
    likely benign LLM, confidence 0.93 Duplicate binary image test data bytes literal.
    Reasoning

    Same binary image test fixture repeated; legitimate test data for image filter validation.

  21. info warn → info by review payload-bytes-literal tests/test_filters.py:788:9

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

        image_data = (
            b'\x00\x00\x00\x010\x00\x01\x00\x00\x00\x13\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06"'
            b'\x00\x01\x00\x00\x00\x1c\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x9f\xa8_\xff\xac'
    likely benign LLM, confidence 0.93 Duplicate binary image test data bytes literal.
    Reasoning

    Same binary image test fixture repeated; legitimate test data for image filter validation.

  22. info warn → info by review payload-bytes-literal tests/test_filters.py:789:9

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

            b'\x00\x00\x00\x010\x00\x01\x00\x00\x00\x13\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x06"'
            b'\x00\x01\x00\x00\x00\x1c\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x9f\xa8_\xff\xac'
    
        )
    likely benign LLM, confidence 0.93 Continuation of binary image test data bytes literal.
    Reasoning

    Continuation of the same binary image test fixture; legitimate for PDF image filter tests.

  23. info warn → info by review payload-bytes-literal tests/test_filters.py:792:21

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

        )
        jbig2_globals = b"\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x18\x00\x00\x03\xff\xfd\xff\x02\xfe\xfe\xfe\x00\x00\x00\x01\x00\x00\x00\x01R\xd0u7\xff\xac"  # noqa: E501
    
        # Validation: Is our image data valid?
    likely benign LLM, confidence 0.93 JBIG2 globals binary test fixture.
    Reasoning

    JBIG2 globals binary fixture used in filter tests; standard test data with no execution path.

  24. info warn → info by review payload-bytes-literal tests/test_filters.py:1086:9

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

        image_data = (
            b"\nBI\n  /W 16\n  /H 16\n  /CS /G\n  /BPC 8\n  /F [/DCT]\nID\n"
            b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x01,\x01,\x00\x00\xff\xfe\x00\x13Created with GIMP\xff\xe2"
    likely benign LLM, confidence 0.94 Inline JPEG image bytes used as a PDF test fixture.
    Reasoning

    Embedded JPEG inline image data in a PDF content stream test; legitimate test fixture.

  25. info warn → info by review payload-bytes-literal tests/test_filters.py:1288:5

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

    _FLATE_IMAGE_DATA = (
        b"\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00"
    likely benign LLM, confidence 0.94 Raw flate image pixel data constant for tests.
    Reasoning

    Flate-encoded image pixel data constant for a test; legitimate binary fixture.

  26. info warn → info by review frame-introspection tests/test_generic.py:1287:5

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

        previous_trace = sys.gettrace()
        sys.settrace(trace)
        try:
            clone = container.clone(writer)
    likely benign LLM, confidence 0.90 Test wraps cloning with a temporary trace function to inspect behavior.
    Reasoning

    Temporarily setting sys.settrace around a clone operation to check for garbage collection or tracing behavior in tests; a benign testing pattern.

  27. info warn → info by review frame-introspection tests/test_generic.py:1291:9

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

        finally:
            sys.settrace(previous_trace)
            if gc_was_enabled:
                gc.enable()
    likely benign LLM, confidence 0.90 Restores the previous trace function after the test clone operation.
    Reasoning

    Restoring the previous trace function in a finally block is the cleanup half of the same benign test wrapper.

  28. info warn → info by review payload-bytes-literal tests/test_images.py:723:13

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

            assert images["~0~"].data == (
                b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x02\x00\x00\x00\x01\x08\x02\x00\x00\x00{@\xe8\xdd\x00\x00\x00\x0f"
                b"IDATx\x9cc\xe8\xee\xee\xfe\xf7\xef\x1f\x00\x0e \x04\x9cpr_\x96\x00\x00\x00\x00IEND\xaeB`\x82"
            )
    likely benign LLM, confidence 0.93 A hardcoded PNG image byte literal used as expected test data in an image test.
    Reasoning

    The bytes literal is a valid PNG file header and IDAT/IEND chunks split across concatenated literals, used as a test fixture for image data. Escapes are the natural way to write binary test data in a test file. No execution or decoding behavior is present.

  29. info unicode-mixed-script tests/test_text_extraction.py:1294:23

    identifier `ابحرمAB` mixes scripts: Arabic + Latin (in string/comment) (raw pass, confidence 0.40)

            pytest.param("AB ابحرم", "AB مرحبا", id="latin-before"),
            pytest.param("ابحرمAB", "مرحباAB", id="latin-after"),
        ],
    )
  30. info unicode-mixed-script tests/test_text_extraction.py:1294:39

    identifier `مرحباAB` mixes scripts: Arabic + Latin (in string/comment) (raw pass, confidence 0.40)

            pytest.param("AB ابحرم", "AB مرحبا", id="latin-before"),
            pytest.param("ابحرمAB", "مرحباAB", id="latin-after"),
        ],
    )
  31. info unicode-mixed-script tests/test_xmp.py:1087:11

    identifier `fooↂzz` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            # Non-hex digits, a marker at the end and a truncated escape are all kept as-is.
            ("fooↂzz", {"fooↂzz": "val"}),
            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
  32. info unicode-mixed-script tests/test_xmp.py:1087:24

    identifier `fooↂzz` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            # Non-hex digits, a marker at the end and a truncated escape are all kept as-is.
            ("fooↂzz", {"fooↂzz": "val"}),
            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
  33. info unicode-mixed-script tests/test_xmp.py:1088:11

    identifier `fooↂ` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            ("fooↂzz", {"fooↂzz": "val"}),
            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
            # A well-formed escape is still decoded (Adobe stores "my car" this way).
  34. info unicode-mixed-script tests/test_xmp.py:1088:22

    identifier `fooↂ` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            ("fooↂzz", {"fooↂzz": "val"}),
            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
            # A well-formed escape is still decoded (Adobe stores "my car" this way).
  35. info unicode-mixed-script tests/test_xmp.py:1089:11

    identifier `fooↂ0` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
            # A well-formed escape is still decoded (Adobe stores "my car" this way).
            ("myↂ0020car", {"my car": "val"}),
  36. info unicode-mixed-script tests/test_xmp.py:1089:23

    identifier `fooↂ0` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            ("fooↂ", {"fooↂ": "val"}),
            ("fooↂ0", {"fooↂ0": "val"}),
            # A well-formed escape is still decoded (Adobe stores "my car" this way).
            ("myↂ0020car", {"my car": "val"}),
  37. info unicode-mixed-script tests/test_xmp.py:1091:11

    identifier `myↂ0020car` mixes scripts: Latin + Other (in string/comment) (raw pass, confidence 0.40)

            # A well-formed escape is still decoded (Adobe stores "my car" this way).
            ("myↂ0020car", {"my car": "val"}),
        ],
    )
  38. identifier `AאA` mixes scripts: Latin + Hebrew (in string/comment) (raw pass, confidence 0.40)

            font=font,
            text_operands="AאA",
            font_size=12,
            rtl_dir=False,
  39. info warn → info by review frame-introspection tests/utils.py:255:5

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

        previous_profile = sys.getprofile()
        sys.setprofile(count_calls)
        try:
            result = func(*args, **kwargs)
    likely benign LLM, confidence 0.90 A test helper that temporarily installs a call-counting profiler via sys.setprofile.
    Reasoning

    Saving the previous profile function and installing a counting profiler callback is a standard test utility for counting function calls. It is not malicious; the frame-introspection signal fires on the sys.setprofile API itself.

  40. info warn → info by review frame-introspection tests/utils.py:259:9

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

        finally:
            sys.setprofile(previous_profile)
        return result, calls
    likely benign LLM, confidence 0.90 Restores the previous profile hook in a finally block after a call-counting test helper runs.
    Reasoning

    The finally block restores the previous profiler, which is the correct cleanup pattern paired with the installation at line 255. This is test instrumentation with no sign of hidden behavior.

Reproduce This Scan

Run disclude locally to verify. 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.1.0
git clone https://github.com/py-pdf/pypdf pypdf && cd pypdf
git checkout 6.21.2
disclude scan --severity info --diff 6.21.1 .