PyPI / psycopg-binary / 3.3.7

psycopg-binary 3.3.7

Source
https://github.com/psycopg/psycopg
Tag scanned
3.3.7
Compared with
3.3.6 (0 signals introduced since)
Files
328 scanned, 49 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 26 info
Scan time
19.4 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 22 of 22 warn and critical signals; 22 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-11-03 06:28

Signals

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

  1. info warn → info by review unicode-zero-width docs/basic/install.rst:76:14

    U+00AD SOFT HYPHEN in source (raw pass, confidence 0.75)

         - Description
         - Perfor⟨U+00AD⟩mance
         - Needs Local libpq?
         - Needs Build Tools?
    likely benign LLM, confidence 0.80 A soft hyphen (U+00AD) splits the word 'Performance' in a documentation table header.
    Reasoning

    A soft hyphen inside the word 'Performance' in an RST table is invisible when rendered and has no functional effect. It most likely came from copying text out of a word processor, not from deliberate obfuscation, though the invisible character is an anomaly worth tidying.

  2. info warn → info by review dynamic-attribute psycopg/psycopg/waiting.py:544:12

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

            )
        wait = globals()[fname]
    
    # On Windows, for the moment, avoid using wait_c, because it was reported to
    dismissed LLM, confidence 0.90 Looks up a wait function by name from module globals, choosing between the C and Python implementations.
    Reasoning

    This selects a wait implementation from the module's own globals by a name chosen internally (fname), which is a common way to pick between C and Python wait backends. No external input reaches it, so it is not a dynamic-execution risk.

  3. info warn → info by review dynamic-attribute tests/crdb/test_copy.py:35:20

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

        with cur.copy(f"copy copy_in from stdin {copyopt(format)}") as copy:
            copy.write(globals()[buffer])
    
        cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 A test looks up a module-level data buffer by parametrized name and writes it to a COPY stream.
    Reasoning

    In a test, globals()[buffer] fetches a module-level test payload chosen by a parametrized name. It is test scaffolding with no external input.

  4. info warn → info by review dynamic-attribute tests/crdb/test_copy_async.py:34:26

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

        async with cur.copy(f"copy copy_in from stdin {copyopt(format)}") as copy:
            await copy.write(globals()[buffer])
    
        await cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 An async test looks up a module-level data buffer by name to write to a COPY stream.
    Reasoning

    Same pattern as the sync variant: a parametrized test fetches a fixed module-level buffer by name for an async COPY write. Benign test code.

  5. info whitespace-anomaly tests/dbapi20.py:6:1

    file mixes tab and space indentation (1 tab-indented and 707 space-indented lines) (raw pass, confidence 0.40)

    
        This software is Public Domain and may be used without restrictions.
    
     "Now we have booze and barflies entering the discussion, plus rumours of
  6. info warn → info by review dynamic-attribute tests/test_copy.py:224:20

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

        with cur.copy(f"copy copy_in from stdin (format {format.name})") as copy:
            copy.write(globals()[buffer])
    
        cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 A test reads a module-level buffer by name and writes it to a COPY stream.
    Reasoning

    A parametrized test fetches a fixed module-level buffer by name for a COPY write. Test code with no external input.

  7. info warn → info by review dynamic-attribute tests/test_copy.py:612:12

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

        file.seek(0)
        want = globals()[buffer]
        got = file.read()
        assert got == want
    dismissed LLM, confidence 0.93 A test reads an expected buffer from module globals to compare with file content.
    Reasoning

    A test fetches an expected value from module globals by name and compares it with file contents. This is ordinary test data lookup.

  8. info warn → info by review dynamic-attribute tests/test_copy.py:781:20

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

            assert not copy.writer._worker
            copy.write(globals()[buffer])
            assert copy.writer._worker
    dismissed LLM, confidence 0.93 A test writes a module-level payload buffer to a COPY writer and asserts on its worker state.
    Reasoning

    A test writes a module-level buffer selected by parametrized name to a COPY writer and checks worker state. Benign test code.

  9. info warn → info by review dynamic-attribute tests/test_copy.py:817:20

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

            assert copy.writer is writer
            copy.write(globals()[buffer])
    
        cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 A test writes a module-level buffer to a COPY writer after checking writer identity.
    Reasoning

    Same parametrized test-buffer pattern as the other COPY tests. No external input reaches the globals lookup.

  10. info warn → info by review dynamic-attribute tests/test_copy_async.py:233:26

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

        async with cur.copy(f"copy copy_in from stdin (format {format.name})") as copy:
            await copy.write(globals()[buffer])
    
        await cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 An async test writes a module-level buffer selected by name to a COPY stream.
    Reasoning

    Parametrized async COPY test writing a fixed module-level buffer. Benign test scaffolding.

  11. info warn → info by review dynamic-attribute tests/test_copy_async.py:625:12

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

        file.seek(0)
        want = globals()[buffer]
        got = file.read()
        assert got == want
    dismissed LLM, confidence 0.93 An async test reads an expected buffer from module globals to compare with file content.
    Reasoning

    An async test loads an expected buffer from module globals to compare with file contents. Ordinary test logic.

  12. info warn → info by review dynamic-attribute tests/test_copy_async.py:799:26

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

            assert not copy.writer._worker
            await copy.write(globals()[buffer])
            assert copy.writer._worker
    dismissed LLM, confidence 0.93 An async test writes a module-level buffer and asserts on the copy worker.
    Reasoning

    Async COPY test writing a module-level buffer and checking writer state. No external input is involved.

  13. info warn → info by review dynamic-attribute tests/test_copy_async.py:836:26

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

            assert copy.writer is writer
            await copy.write(globals()[buffer])
    
        await cur.execute("select * from copy_in order by 1")
    dismissed LLM, confidence 0.93 An async test writes a module-level buffer to a COPY writer after an identity check.
    Reasoning

    Same parametrized async test-buffer pattern. Benign.

  14. info critical → info by review dynamic-import tests/test_module.py:48:16

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

        packagename = _psycopg.__name__.split(".")[0]
        cpackage = __import__(packagename)
        assert cpackage.__version__.startswith("3.")
    dismissed LLM, confidence 0.92 Imports the psycopg package by a name derived from its own C extension module name to check its version.
    Reasoning

    __import__ receives the package name derived from the psycopg C extension's own __name__ split on '.', which is a fixed internal value, not attacker input. This is a standard way to check the installed package version in a test.

  15. info warn → info by review dynamic-execution tests/test_waiting.py:375:30

    `open(__file__)` — file reads its own source, common pattern for extracting payloads hidden in comments (ast pass, confidence 0.85)

                for i in range(1100):
                    files.append(open(__file__))
            except OSError:
                pytest.skip("can't open the number of files needed for the test")
    dismissed LLM, confidence 0.88 Opens the test file repeatedly to exhaust file descriptors and test OSError handling.
    Reasoning

    Opening __file__ in a loop is a standard way to exhaust file descriptors to test the handling of EMFILE-like errors. The payload-extraction concern in the message does not apply, since the file contents are never parsed or executed.

  16. info warn → info by review dynamic-execution tests/test_waiting_async.py:384:30

    `open(__file__)` — file reads its own source, common pattern for extracting payloads hidden in comments (ast pass, confidence 0.85)

                for i in range(1100):
                    files.append(open(__file__))
            except OSError:
                pytest.skip("can't open the number of files needed for the test")
    dismissed LLM, confidence 0.88 Opens the test file repeatedly to exhaust file descriptors in an async waiting test.
    Reasoning

    The same file-descriptor exhaustion pattern as the sync test: __file__ is opened only to consume descriptors, and its contents are never executed or decoded.

  17. info warn → info by review dynamic-attribute tests/types/test_shapely.py:107:11

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

    def test_with_adapter(shapely_conn, obj, fmt_in):
        obj = globals()[obj]
        with shapely_conn.cursor() as cur:
            cur.execute(f"SELECT pg_typeof(%{fmt_in.value})", [obj])
    dismissed LLM, confidence 0.93 A shapely adapter test looks up a test geometry object by parametrized name from globals.
    Reasoning

    A parametrized test resolves a fixed test geometry object by name from module globals and passes it to a query. Test code with no external input.

  18. info warn → info by review dynamic-attribute tests/types/test_shapely.py:120:11

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

    def test_write_read_shape(shapely_conn, fmt_in, fmt_out, obj, srid):
        obj = globals()[obj]
        with shapely_conn.cursor(binary=fmt_out) as cur:
            cur.execute("drop table if exists sample_geoms")
    likely benign LLM, confidence 0.80 A test resolves a parametrized object name through globals() to pick a test geometry.
    Reasoning

    globals() lookup keyed by a test parameter name is a common pytest idiom for selecting test fixtures or objects by name. The key comes from the test's own parametrization, not untrusted input, and the code only builds test data.

  19. info dynamic-execution tools/ci/ci_test.sh:24:1

    command name is a variable (`$pytest`) that this file sets only to plain text (ast pass, confidence 0.40)

    
    $pytest -m "$markers" "$@" && exit 0
    
    $pytest -m "$markers" --lf --randomly-seed=last "$@" && exit 0
  20. info dynamic-execution tools/ci/ci_test.sh:26:1

    command name is a variable (`$pytest`) that this file sets only to plain text (ast pass, confidence 0.40)

    
    $pytest -m "$markers" --lf --randomly-seed=last "$@" && exit 0
    
    $pytest -m "$markers" --lf --randomly-seed=last "$@"
  21. info dynamic-execution tools/ci/ci_test.sh:28:1

    command name is a variable (`$pytest`) that this file sets only to plain text (ast pass, confidence 0.40)

    
    $pytest -m "$markers" --lf --randomly-seed=last "$@"
  22. info critical → info by review dynamic-execution tools/ci/strip_wheel.sh:37:1

    command name is a variable (`$dir`) and `dir` is assigned the output of a command — executed command determined at runtime (ast pass, confidence 0.90)

    Dependencies versions of '_psycopg.so' library:"
    "${dir}/print_so_versions.sh" "$(find . -name \*_psycopg\*.so)"
    
    find . -name \*.so -exec strip "$@" {} \;
    likely benign LLM, confidence 0.85 A CI script runs a sibling helper script via a directory variable and strips shared libraries in a built wheel.
    Reasoning

    The variable dir is almost certainly set from dirname of the script location in the psycopg CI tooling, so the script runs its own helper print_so_versions.sh. The find and strip calls are ordinary wheel-slimming steps. The assignment of dir is not shown, but the pattern matches legitimate CI scripts.

  23. info critical → info by review dynamic-execution tools/ci/wheel_linux_before_all.sh:16:9

    command name is a variable (`$dir`) and `dir` is assigned the output of a command — executed command determined at runtime (ast pass, confidence 0.90)

            # tzdata is required for datetime tests.
            "${dir}/build_libpq.sh" > /dev/null
            ;;
    likely benign LLM, confidence 0.85 A Linux wheel-build CI script invokes a local build_libpq.sh via a directory variable.
    Reasoning

    The command path is a directory variable used to invoke build_libpq.sh, a sibling build script in the same repository. This is standard CI layout. Nothing in the snippet fetches remote code or decodes payloads.

  24. info critical → info by review dynamic-execution tools/ci/wheel_linux_before_all.sh:38:9

    command name is a variable (`$dir`) and `dir` is assigned the output of a command — executed command determined at runtime (ast pass, confidence 0.90)

        centos | rocky | almalinux)
            "${dir}/build_libpq.sh" > /dev/null
            ;;
    likely benign LLM, confidence 0.85 A distro-specific branch of the Linux CI script runs the local build_libpq.sh via a directory variable.
    Reasoning

    This is the same pattern as the earlier finding in the same file: the directory variable points to the repository's own build_libpq.sh in a distro-specific branch. No network fetch or obfuscation appears in the snippet.

  25. info critical → info by review dynamic-execution tools/ci/wheel_macos_before_all.sh:14:1

    command name is a variable (`$prjdir`) and `prjdir` is assigned the output of a command — executed command determined at runtime (ast pass, confidence 0.90)

    # Build dependency libraries
    "${prjdir}/tools/ci/build_libpq.sh"
    
    # Show dependency tree
    likely benign LLM, confidence 0.85 A macOS CI script runs the repository's build_libpq.sh through a project-directory variable.
    Reasoning

    prjdir is most likely the project root derived from the script location, and the command runs the repository's own build_libpq.sh. This is a routine dependency build step in macOS CI.

  26. info warn → info by review dynamic-execution tools/ci/wheel_macos_before_all.sh:27:3

    `eval` called on a string literal (ast pass, confidence 0.85)

    for i in $(seq 10 -1 0); do
      eval pg_isready && break
      if [ $i == 0 ]; then
          echo "PostgreSQL service not ready, giving up"
    likely benign LLM, confidence 0.90 A retry loop uses eval on the literal pg_isready command while waiting for a PostgreSQL service.
    Reasoning

    eval is applied to a fixed literal command, pg_isready, inside a retry loop waiting for PostgreSQL to start. This is redundant but harmless. It takes no external input and has no malicious characteristics.

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.3.0
git clone https://github.com/psycopg/psycopg psycopg && cd psycopg
git checkout 3.3.7
disclude scan --untrusted --severity info --diff 3.3.6 .