Security advisory GHSA-wq2g-r956-j8cc: pg8000 SQL injection vulnerability via a specially crafted Python list input
Package: pg8000 (pypi), vulnerable version: 1.31.4, fixed in: 1.31.5
CWE: CWE-89
Files changed by the fix:
b/src/pg8000/converters.py
b/src/pg8000/core.py
b/test/test_converters.py

Fix diff:
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/.github/workflows/test.yml /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/.github/workflows/test.yml
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,191 +0,0 @@
-name: pg8000
-
-on: [push]
-
-permissions: read-all
-
-jobs:
-  main-test:
-
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
-        postgresql-version: [17, 16, 15, 14, 13]
-
-    container:
-      image: python:${{ matrix.python-version }}
-      env:
-        PGHOST: postgres
-        PGPASSWORD: postgres
-        PGUSER: postgres
-
-    services:
-      postgres:
-        image: postgres:${{ matrix.postgresql-version }}
-        env:
-          POSTGRES_PASSWORD: postgres
-        # Set health checks to wait until postgres has started
-        options: >-
-          --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-
-    steps:
-      - uses: actions/checkout@v4
-      - name: Install dependencies
-        run: |
-          git config --global --add safe.directory "$GITHUB_WORKSPACE"
-          python -m pip install --no-cache-dir --upgrade pip
-          pip install --no-cache-dir --root-user-action ignore pytest pytest-mock pytest-benchmark pytz .
-      - name: Set up Postgresql
-        run: |
-          apt-get update
-          apt-get install --yes --no-install-recommends postgresql-client
-          psql -c "CREATE EXTENSION hstore;"
-          psql -c "SELECT pg_reload_conf()"
-      - name: Test with pytest
-        run: |
-          python -m pytest -x -v -W error --ignore=test/dbapi/auth/ --ignore=test/legacy/auth/ --ignore=test/native/auth/ test --ignore=test/test_readme.py
-
-  auth-test:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python-version: ["3.13"]
-        postgresql-version: ["17", "13"]
-        auth-type: [md5, gss, password, scram-sha-256]
-
-    container:
-      image: python:${{ matrix.python-version }}
-      env:
-        PGHOST: postgres
-        PGPASSWORD: postgres
-        PGUSER: postgres
-        PIP_ROOT_USER_ACTION: ignore
-
-    services:
-      postgres:
-        image: postgres:${{ matrix.postgresql-version }}
-        env:
-          POSTGRES_PASSWORD: postgres
-          POSTGRES_HOST_AUTH_METHOD: ${{ matrix.auth-type }}
-          POSTGRES_INITDB_ARGS: "${{ matrix.auth-type == 'scram-sha-256' && '--auth-host=scram-sha-256' || '' }}"
-        # Set health checks to wait until postgres has started
-        options: >-
-          --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-
-    steps:
-      - uses: actions/checkout@v4
-      - name: Install dependencies
-        run: |
-          git config --global --add safe.directory "$GITHUB_WORKSPACE"
-          python -m pip install --no-cache-dir --upgrade pip
-          pip install --no-cache-dir pytest pytest-mock pytest-benchmark pytz .
-      - name: Test with pytest
-        run: |
-          python -m pytest -x -v -W error test/dbapi/auth/test_${{ matrix.auth-type }}.py test/native/auth/test_${{ matrix.auth-type }}.py test/legacy/auth/test_${{ matrix.auth-type }}.py
-
-  ssl-test:
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        auth-type: [md5, scram-sha-256]
-
-    services:
-      postgres:
-        image: postgres
-        env:
-          POSTGRES_PASSWORD: postgres
-          POSTGRES_HOST_AUTH_METHOD: ${{ matrix.auth-type }}
-          POSTGRES_INITDB_ARGS: "${{ matrix.auth-type == 'scram-sha-256' && '--auth-host=scram-sha-256' || '' }}"
-        options: >-
-          --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-        ports:
-          - 5432:5432
-
-    steps:
-      - name: Configure Postgres
-        env:
-          PGPASSWORD: postgres
-          PGUSER: postgres
-          PGHOST: localhost
-        run: |
-          sudo apt update
-          sudo apt install --yes --no-install-recommends postgresql-client
-          psql -c "ALTER SYSTEM SET ssl = on;"
-          psql -c "ALTER SYSTEM SET ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'"
-          psql -c "ALTER SYSTEM SET ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'"
-          psql -c "SELECT pg_reload_conf()"
-
-      - name: Check out repository code
-        uses: actions/checkout@v4
-      - name: Set up Python
-        uses: actions/setup-python@v5
-        with:
-          python-version: "3.13"
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install pytest pytest-mock pytest-benchmark pytz .
-      - name: SSL Test
-        env:
-          PGPASSWORD: postgres
-          USER: postgres
-        run: |
-          python -m pytest -x -v -W error test/dbapi/auth/test_${{ matrix.auth-type}}_ssl.py test/native/auth/test_${{ matrix.auth-type}}_ssl.py test/legacy/auth/test_${{ matrix.auth-type}}_ssl.py
-
-  static-test:
-    runs-on: ubuntu-latest
-
-    services:
-      postgres:
-        image: postgres:12
-        env:
-          POSTGRES_PASSWORD: cpsnow
-        options: >-
-          --health-cmd pg_isready
-          --health-interval 10s
-          --health-timeout 5s
-          --health-retries 5
-        ports:
-          - 5432:5432
-
-    steps:
-      - name: Check out repository code
-        uses: actions/checkout@v4
-      - name: Set up Python
-        uses: actions/setup-python@v5
-        with:
-          python-version: "3.13"
-      - name: Install dependencies
-        run: |
-          psql postgresql://postgres:cpsnow@localhost -c "ALTER SYSTEM SET ssl = on;"
-          psql postgresql://postgres:cpsnow@localhost -c "ALTER SYSTEM SET ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'"
-          psql postgresql://postgres:cpsnow@localhost -c "ALTER SYSTEM SET ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'"
-          psql postgresql://postgres:cpsnow@localhost localhost -c "SELECT pg_reload_conf()"
-          python -m pip install --upgrade pip
-          pip install black build flake8 pytest flake8-alphabetize Flake8-pyproject \
-            twine .
-      - name: Lint check
-        run: |
-          black --check .
-          flake8 .
-      - name: Doctest
-        env:
-          PGPASSWORD: cpsnow
-          LOGNAME: postgres
-        run: |
-          python -m pytest -x -v -W error test/test_readme.py
-      - name: Check Distribution
-        run: |
-          python -m build
-          twine check dist/*
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/PKG-INFO /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/PKG-INFO
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,8 +1,8 @@
 Metadata-Version: 2.4
 Name: pg8000
-Version: 1.31.4
+Version: 1.31.5
 Summary: PostgreSQL interface library
-Project-URL: Homepage, https://github.com/tlocke/pg8000
+Project-URL: Homepage, https://codeberg.org/tlocke/pg8000
 Author: The Contributors
 License: BSD 3-Clause License
 License-File: LICENSE
@@ -36,10 +36,172 @@
 belief that it is probably about the 8000th PostgreSQL interface for Python. pg8000 is
 distributed under the BSD 3-clause license.
 
-All bug reports, feature requests and contributions are welcome at
-[http://github.com/tlocke/pg8000/](http://github.com/tlocke/pg8000/).
+<!-- mtoc-start -->
 
-[![Workflow Status Badge](https://github.com/tlocke/pg8000/actions/workflows/test.yml/badge.svg)](https://github.com/tlocke/pg8000/actions/workflows/test.yml)
+* [Installation](#installation)
+* [Native API Interactive Examples](#native-api-interactive-examples)
+  * [Basic Example](#basic-example)
+  * [Transactions](#transactions)
+  * [Query Using Functions](#query-using-functions)
+  * [Interval Type](#interval-type)
+  * [Point Type](#point-type)
+  * [Client Encoding](#client-encoding)
+  * [JSON](#json)
+  * [Retrieve Column Metadata From Results](#retrieve-column-metadata-from-results)
+  * [Notices And Notifications](#notices-and-notifications)
+  * [Parameter Statuses](#parameter-statuses)
+  * [LIMIT ALL](#limit-all)
+  * [IN and NOT IN](#in-and-not-in)
+  * [Many SQL Statements Can't Be Parameterized](#many-sql-statements-cant-be-parameterized)
+  * [COPY FROM And TO A Stream](#copy-from-and-to-a-stream)
+  * [Execute Multiple SQL Statements](#execute-multiple-sql-statements)
+  * [Quoted Identifiers in SQL](#quoted-identifiers-in-sql)
+  * [Custom adapter from a Python type to a PostgreSQL type](#custom-adapter-from-a-python-type-to-a-postgresql-type)
+  * [Custom adapter from a PostgreSQL type to a Python type](#custom-adapter-from-a-postgresql-type-to-a-python-type)
+  * [Could Not Determine Data Type Of Parameter](#could-not-determine-data-type-of-parameter)
+  * [Prepared Statements](#prepared-statements)
+  * [Use Environment Variables As Connection Defaults](#use-environment-variables-as-connection-defaults)
+  * [Connect To PostgreSQL Over SSL](#connect-to-postgresql-over-ssl)
+  * [Server-Side Cursors](#server-side-cursors)
+  * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects)
+  * [Replication Protocol](#replication-protocol)
+  * [Extra Startup Parameters](#extra-startup-parameters)
+  * [PostgreSQL-Style Parameter Placeholders](#postgresql-style-parameter-placeholders)
+* [DB-API 2 Interactive Examples](#db-api-2-interactive-examples)
+  * [Basic Example](#basic-example-1)
+  * [Query Using Functions](#query-using-functions-1)
+  * [Interval Type](#interval-type-1)
+  * [Point Type](#point-type-1)
+  * [Numeric Parameter Style](#numeric-parameter-style)
+  * [Autocommit](#autocommit)
+  * [Client Encoding](#client-encoding-1)
+  * [JSON](#json-1)
+  * [Retrieve Column Names From Results](#retrieve-column-names-from-results)
+  * [COPY from and to a file](#copy-from-and-to-a-file)
+  * [Server-Side Cursors](#server-side-cursors-1)
+  * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects-1)
+  * [Parameter Limit](#parameter-limit)
+* [Type Mapping](#type-mapping)
+* [Theory Of Operation](#theory-of-operation)
+* [Native API Docs](#native-api-docs)
+  * [pg8000.native.Error](#pg8000nativeerror)
+  * [pg8000.native.InterfaceError](#pg8000nativeinterfaceerror)
+  * [pg8000.native.DatabaseError](#pg8000nativedatabaseerror)
+  * [pg8000.native.Connection(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, application\_name=None, replication=None, sock=None)](#pg8000nativeconnectionuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-application_namenone-replicationnone-socknone)
+  * [pg8000.native.Connection.notifications](#pg8000nativeconnectionnotifications)
+  * [pg8000.native.Connection.notices](#pg8000nativeconnectionnotices)
+  * [pg8000.native.Connection.parameter\_statuses](#pg8000nativeconnectionparameter_statuses)
+  * [pg8000.native.Connection.run(sql, stream=None, types=None, \*\*kwargs)](#pg8000nativeconnectionrunsql-streamnone-typesnone-kwargs)
+  * [pg8000.native.Connection.row\_count](#pg8000nativeconnectionrow_count)
+  * [pg8000.native.Connection.columns](#pg8000nativeconnectioncolumns)
+  * [pg8000.native.Connection.close()](#pg8000nativeconnectionclose)
+  * [pg8000.native.Connection.register\_out\_adapter(typ, out\_func)](#pg8000nativeconnectionregister_out_adaptertyp-out_func)
+  * [pg8000.native.Connection.register\_in\_adapter(oid, in\_func)](#pg8000nativeconnectionregister_in_adapteroid-in_func)
+  * [pg8000.native.Connection.prepare(sql)](#pg8000nativeconnectionpreparesql)
+  * [pg8000.native.PreparedStatement](#pg8000nativepreparedstatement)
+    * [pg8000.native.PreparedStatement.run(\*\*kwargs)](#pg8000nativepreparedstatementrunkwargs)
+    * [pg8000.native.PreparedStatement.close()](#pg8000nativepreparedstatementclose)
+  * [pg8000.native.identifier(ident)](#pg8000nativeidentifierident)
+  * [pg8000.native.literal(value)](#pg8000nativeliteralvalue)
+* [DB-API 2 Docs](#db-api-2-docs)
+  * [Properties](#properties)
+    * [pg8000.dbapi.apilevel](#pg8000dbapiapilevel)
+    * [pg8000.dbapi.threadsafety](#pg8000dbapithreadsafety)
+    * [pg8000.dbapi.paramstyle](#pg8000dbapiparamstyle)
+    * [pg8000.dbapi.STRING](#pg8000dbapistring)
+    * [pg8000.dbapi.BINARY](#pg8000dbapibinary)
+    * [pg8000.dbapi.NUMBER](#pg8000dbapinumber)
+    * [pg8000.dbapi.DATETIME](#pg8000dbapidatetime)
+    * [pg8000.dbapi.ROWID](#pg8000dbapirowid)
+  * [Functions](#functions)
+    * [pg8000.dbapi.connect(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, applicationa_name=None, replication=None, sock=None)](#pg8000dbapiconnectuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-applicationa_namenone-replicationnone-socknone)
+    * [pg8000.dbapi.Date(year, month, day)](#pg8000dbapidateyear-month-day)
+    * [pg8000.dbapi.Time(hour, minute, second)](#pg8000dbapitimehour-minute-second)
+    * [pg8000.dbapi.Timestamp(year, month, day, hour, minute, second)](#pg8000dbapitimestampyear-month-day-hour-minute-second)
+    * [pg8000.dbapi.DateFromTicks(ticks)](#pg8000dbapidatefromticksticks)
+    * [pg8000.dbapi.TimeFromTicks(ticks)](#pg8000dbapitimefromticksticks)
+    * [pg8000.dbapi.TimestampFromTicks(ticks)](#pg8000dbapitimestampfromticksticks)
+    * [pg8000.dbapi.Binary(value)](#pg8000dbapibinaryvalue)
+  * [Generic Exceptions](#generic-exceptions)
+    * [pg8000.dbapi.Warning](#pg8000dbapiwarning)
+    * [pg8000.dbapi.Error](#pg8000dbapierror)
+    * [pg8000.dbapi.InterfaceError](#pg8000dbapiinterfaceerror)
+    * [pg8000.dbapi.DatabaseError](#pg8000dbapidatabaseerror)
+    * [pg8000.dbapi.DataError](#pg8000dbapidataerror)
+    * [pg8000.dbapi.OperationalError](#pg8000dbapioperationalerror)
+    * [pg8000.dbapi.IntegrityError](#pg8000dbapiintegrityerror)
+    * [pg8000.dbapi.InternalError](#pg8000dbapiinternalerror)
+    * [pg8000.dbapi.ProgrammingError](#pg8000dbapiprogrammingerror)
+    * [pg8000.dbapi.NotSupportedError](#pg8000dbapinotsupportederror)
+  * [Classes](#classes)
+    * [pg8000.dbapi.Connection](#pg8000dbapiconnection)
+    * [pg8000.dbapi.Connection.autocommit](#pg8000dbapiconnectionautocommit)
+    * [pg8000.dbapi.Connection.close()](#pg8000dbapiconnectionclose)
+    * [pg8000.dbapi.Connection.cursor()](#pg8000dbapiconnectioncursor)
+    * [pg8000.dbapi.Connection.rollback()](#pg8000dbapiconnectionrollback)
+    * [pg8000.dbapi.Connection.tpc\_begin(xid)](#pg8000dbapiconnectiontpc_beginxid)
+    * [pg8000.dbapi.Connection.tpc_commit(xid=None)](#pg8000dbapiconnectiontpc_commitxidnone)
+    * [pg8000.dbapi.Connection.tpc_prepare()](#pg8000dbapiconnectiontpc_prepare)
+    * [pg8000.dbapi.Connection.tpc_recover()](#pg8000dbapiconnectiontpc_recover)
+    * [pg8000.dbapi.Connection.tpc_rollback(xid=None)](#pg8000dbapiconnectiontpc_rollbackxidnone)
+    * [pg8000.dbapi.Connection.xid(format_id, global_transaction_id, branch_qualifier)](#pg8000dbapiconnectionxidformat_id-global_transaction_id-branch_qualifier)
+    * [pg8000.dbapi.Cursor](#pg8000dbapicursor)
+      * [pg8000.dbapi.Cursor.arraysize](#pg8000dbapicursorarraysize)
+      * [pg8000.dbapi.Cursor.connection](#pg8000dbapicursorconnection)
+      * [pg8000.dbapi.Cursor.rowcount](#pg8000dbapicursorrowcount)
+      * [pg8000.dbapi.Cursor.description](#pg8000dbapicursordescription)
+      * [pg8000.dbapi.Cursor.close()](#pg8000dbapicursorclose)
+      * [pg8000.dbapi.Cursor.execute(operation, args=None, stream=None)](#pg8000dbapicursorexecuteoperation-argsnone-streamnone)
+      * [pg8000.dbapi.Cursor.executemany(operation, param_sets)](#pg8000dbapicursorexecutemanyoperation-param_sets)
+      * [pg8000.dbapi.Cursor.callproc(procname, parameters=None)](#pg8000dbapicursorcallprocprocname-parametersnone)
+      * [pg8000.dbapi.Cursor.fetchall()](#pg8000dbapicursorfetchall)
+      * [pg8000.dbapi.Cursor.fetchmany(size=None)](#pg8000dbapicursorfetchmanysizenone)
+      * [pg8000.dbapi.Cursor.fetchone()](#pg8000dbapicursorfetchone)
+      * [pg8000.dbapi.Cursor.setinputsizes(\*sizes)](#pg8000dbapicursorsetinputsizessizes)
+      * [pg8000.dbapi.Cursor.setoutputsize(size, column=None)](#pg8000dbapicursorsetoutputsizesize-columnnone)
+    * [pg8000.dbapi.Interval](#pg8000dbapiinterval)
+* [Design Decisions](#design-decisions)
+* [Tests](#tests)
+* [Doing A Release Of pg8000](#doing-a-release-of-pg8000)
+* [Release Notes](#release-notes)
+  * [Version 1.31.5, 2025-09-14](#version-1315-2025-09-14)
+  * [Version 1.31.4, 2025-07-20](#version-1314-2025-07-20)
+  * [Version 1.31.3, 2025-07-19](#version-1313-2025-07-19)
+  * [Version 1.31.2, 2024-04-28](#version-1312-2024-04-28)
+  * [Version 1.31.1, 2024-04-01](#version-1311-2024-04-01)
+  * [Version 1.31.0, 2024-03-31](#version-1310-2024-03-31)
+  * [Version 1.30.5, 2024-02-22](#version-1305-2024-02-22)
+  * [Version 1.30.4, 2024-01-03](#version-1304-2024-01-03)
+  * [Version 1.30.3, 2023-10-31](#version-1303-2023-10-31)
+  * [Version 1.30.2, 2023-09-17](#version-1302-2023-09-17)
+  * [Version 1.30.1, 2023-07-29](#version-1301-2023-07-29)
+  * [Version 1.30.0, 2023-07-27](#version-1300-2023-07-27)
+  * [Version 1.29.8, 2023-06-16](#version-1298-2023-06-16)
+  * [Version 1.29.7, 2023-06-16](#version-1297-2023-06-16)
+  * [Version 1.29.6, 2023-05-29](#version-1296-2023-05-29)
+  * [Version 1.29.5, 2023-05-09](#version-1295-2023-05-09)
+  * [Version 1.29.4, 2022-12-14](#version-1294-2022-12-14)
+  * [Version 1.29.3, 2022-10-26](#version-1293-2022-10-26)
+  * [Version 1.29.2, 2022-10-09](#version-1292-2022-10-09)
+  * [Version 1.29.1, 2022-05-23](#version-1291-2022-05-23)
+  * [Version 1.29.0, 2022-05-21](#version-1290-2022-05-21)
+  * [Version 1.28.3, 2022-05-18](#version-1283-2022-05-18)
+  * [Version 1.28.2, 2022-05-17](#version-1282-2022-05-17)
+  * [Version 1.28.1, 2022-05-17](#version-1281-2022-05-17)
+  * [Version 1.28.0, 2022-05-17](#version-1280-2022-05-17)
+  * [Version 1.27.1, 2022-05-16](#version-1271-2022-05-16)
+  * [Version 1.27.0, 2022-05-16](#version-1270-2022-05-16)
+  * [Version 1.26.1, 2022-04-23](#version-1261-2022-04-23)
+  * [Version 1.26.0, 2022-04-18](#version-1260-2022-04-18)
+  * [Version 1.25.0, 2022-04-17](#version-1250-2022-04-17)
+  * [Version 1.24.2, 2022-04-15](#version-1242-2022-04-15)
+  * [Version 1.24.1, 2022-03-02](#version-1241-2022-03-02)
+  * [Version 1.24.0, 2022-02-06](#version-1240-2022-02-06)
+  * [Version 1.23.0, 2021-11-13](#version-1230-2021-11-13)
+  * [Version 1.22.1, 2021-11-10](#version-1221-2021-11-10)
+  * [Version 1.22.0, 2021-10-13](#version-1220-2021-10-13)
+
+<!-- mtoc-end -->
 
 ## Installation
 
@@ -140,7 +302,7 @@
 
 ```
 
-NB. There is [a longstanding bug](https://github.com/tlocke/pg8000/issues/36>) in the
+NB. There is [a longstanding bug](https://codeberg.org/tlocke/pg8000/issues/36) in the
 PostgreSQL server whereby if a `COMMIT` is issued against a failed transaction, the
 transaction is silently rolled back, rather than an error being returned. pg8000
 attempts to detect when this has happened and raise an `InterfaceError`.
@@ -2137,6 +2299,13 @@
 
 ## Release Notes
 
+### Version 1.31.5, 2025-09-14
+
+- Tiny performance improvement in reading from socket.
+- Fix bug in `literal()` where list is not properly escaped.
+- Move to [Codeberg](https://codeberg.org/tlocke/pg8000).
+
+
 ### Version 1.31.4, 2025-07-20
 
 - Various speed optimisations.
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/README.md /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/README.md
--- a/README.md
+++ b/README.md
@@ -6,10 +6,172 @@
 belief that it is probably about the 8000th PostgreSQL interface for Python. pg8000 is
 distributed under the BSD 3-clause license.
 
-All bug reports, feature requests and contributions are welcome at
-[http://github.com/tlocke/pg8000/](http://github.com/tlocke/pg8000/).
+<!-- mtoc-start -->
 
-[![Workflow Status Badge](https://github.com/tlocke/pg8000/actions/workflows/test.yml/badge.svg)](https://github.com/tlocke/pg8000/actions/workflows/test.yml)
+* [Installation](#installation)
+* [Native API Interactive Examples](#native-api-interactive-examples)
+  * [Basic Example](#basic-example)
+  * [Transactions](#transactions)
+  * [Query Using Functions](#query-using-functions)
+  * [Interval Type](#interval-type)
+  * [Point Type](#point-type)
+  * [Client Encoding](#client-encoding)
+  * [JSON](#json)
+  * [Retrieve Column Metadata From Results](#retrieve-column-metadata-from-results)
+  * [Notices And Notifications](#notices-and-notifications)
+  * [Parameter Statuses](#parameter-statuses)
+  * [LIMIT ALL](#limit-all)
+  * [IN and NOT IN](#in-and-not-in)
+  * [Many SQL Statements Can't Be Parameterized](#many-sql-statements-cant-be-parameterized)
+  * [COPY FROM And TO A Stream](#copy-from-and-to-a-stream)
+  * [Execute Multiple SQL Statements](#execute-multiple-sql-statements)
+  * [Quoted Identifiers in SQL](#quoted-identifiers-in-sql)
+  * [Custom adapter from a Python type to a PostgreSQL type](#custom-adapter-from-a-python-type-to-a-postgresql-type)
+  * [Custom adapter from a PostgreSQL type to a Python type](#custom-adapter-from-a-postgresql-type-to-a-python-type)
+  * [Could Not Determine Data Type Of Parameter](#could-not-determine-data-type-of-parameter)
+  * [Prepared Statements](#prepared-statements)
+  * [Use Environment Variables As Connection Defaults](#use-environment-variables-as-connection-defaults)
+  * [Connect To PostgreSQL Over SSL](#connect-to-postgresql-over-ssl)
+  * [Server-Side Cursors](#server-side-cursors)
+  * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects)
+  * [Replication Protocol](#replication-protocol)
+  * [Extra Startup Parameters](#extra-startup-parameters)
+  * [PostgreSQL-Style Parameter Placeholders](#postgresql-style-parameter-placeholders)
+* [DB-API 2 Interactive Examples](#db-api-2-interactive-examples)
+  * [Basic Example](#basic-example-1)
+  * [Query Using Functions](#query-using-functions-1)
+  * [Interval Type](#interval-type-1)
+  * [Point Type](#point-type-1)
+  * [Numeric Parameter Style](#numeric-parameter-style)
+  * [Autocommit](#autocommit)
+  * [Client Encoding](#client-encoding-1)
+  * [JSON](#json-1)
+  * [Retrieve Column Names From Results](#retrieve-column-names-from-results)
+  * [COPY from and to a file](#copy-from-and-to-a-file)
+  * [Server-Side Cursors](#server-side-cursors-1)
+  * [BLOBs (Binary Large Objects)](#blobs-binary-large-objects-1)
+  * [Parameter Limit](#parameter-limit)
+* [Type Mapping](#type-mapping)
+* [Theory Of Operation](#theory-of-operation)
+* [Native API Docs](#native-api-docs)
+  * [pg8000.native.Error](#pg8000nativeerror)
+  * [pg8000.native.InterfaceError](#pg8000nativeinterfaceerror)
+  * [pg8000.native.DatabaseError](#pg8000nativedatabaseerror)
+  * [pg8000.native.Connection(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, application\_name=None, replication=None, sock=None)](#pg8000nativeconnectionuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-application_namenone-replicationnone-socknone)
+  * [pg8000.native.Connection.notifications](#pg8000nativeconnectionnotifications)
+  * [pg8000.native.Connection.notices](#pg8000nativeconnectionnotices)
+  * [pg8000.native.Connection.parameter\_statuses](#pg8000nativeconnectionparameter_statuses)
+  * [pg8000.native.Connection.run(sql, stream=None, types=None, \*\*kwargs)](#pg8000nativeconnectionrunsql-streamnone-typesnone-kwargs)
+  * [pg8000.native.Connection.row\_count](#pg8000nativeconnectionrow_count)
+  * [pg8000.native.Connection.columns](#pg8000nativeconnectioncolumns)
+  * [pg8000.native.Connection.close()](#pg8000nativeconnectionclose)
+  * [pg8000.native.Connection.register\_out\_adapter(typ, out\_func)](#pg8000nativeconnectionregister_out_adaptertyp-out_func)
+  * [pg8000.native.Connection.register\_in\_adapter(oid, in\_func)](#pg8000nativeconnectionregister_in_adapteroid-in_func)
+  * [pg8000.native.Connection.prepare(sql)](#pg8000nativeconnectionpreparesql)
+  * [pg8000.native.PreparedStatement](#pg8000nativepreparedstatement)
+    * [pg8000.native.PreparedStatement.run(\*\*kwargs)](#pg8000nativepreparedstatementrunkwargs)
+    * [pg8000.native.PreparedStatement.close()](#pg8000nativepreparedstatementclose)
+  * [pg8000.native.identifier(ident)](#pg8000nativeidentifierident)
+  * [pg8000.native.literal(value)](#pg8000nativeliteralvalue)
+* [DB-API 2 Docs](#db-api-2-docs)
+  * [Properties](#properties)
+    * [pg8000.dbapi.apilevel](#pg8000dbapiapilevel)
+    * [pg8000.dbapi.threadsafety](#pg8000dbapithreadsafety)
+    * [pg8000.dbapi.paramstyle](#pg8000dbapiparamstyle)
+    * [pg8000.dbapi.STRING](#pg8000dbapistring)
+    * [pg8000.dbapi.BINARY](#pg8000dbapibinary)
+    * [pg8000.dbapi.NUMBER](#pg8000dbapinumber)
+    * [pg8000.dbapi.DATETIME](#pg8000dbapidatetime)
+    * [pg8000.dbapi.ROWID](#pg8000dbapirowid)
+  * [Functions](#functions)
+    * [pg8000.dbapi.connect(user, host='localhost', database=None, port=5432, password=None, source\_address=None, unix\_sock=None, ssl\_context=None, timeout=None, tcp\_keepalive=True, applicationa_name=None, replication=None, sock=None)](#pg8000dbapiconnectuser-hostlocalhost-databasenone-port5432-passwordnone-source_addressnone-unix_socknone-ssl_contextnone-timeoutnone-tcp_keepalivetrue-applicationa_namenone-replicationnone-socknone)
+    * [pg8000.dbapi.Date(year, month, day)](#pg8000dbapidateyear-month-day)
+    * [pg8000.dbapi.Time(hour, minute, second)](#pg8000dbapitimehour-minute-second)
+    * [pg8000.dbapi.Timestamp(year, month, day, hour, minute, second)](#pg8000dbapitimestampyear-month-day-hour-minute-second)
+    * [pg8000.dbapi.DateFromTicks(ticks)](#pg8000dbapidatefromticksticks)
+    * [pg8000.dbapi.TimeFromTicks(ticks)](#pg8000dbapitimefromticksticks)
+    * [pg8000.dbapi.TimestampFromTicks(ticks)](#pg8000dbapitimestampfromticksticks)
+    * [pg8000.dbapi.Binary(value)](#pg8000dbapibinaryvalue)
+  * [Generic Exceptions](#generic-exceptions)
+    * [pg8000.dbapi.Warning](#pg8000dbapiwarning)
+    * [pg8000.dbapi.Error](#pg8000dbapierror)
+    * [pg8000.dbapi.InterfaceError](#pg8000dbapiinterfaceerror)
+    * [pg8000.dbapi.DatabaseError](#pg8000dbapidatabaseerror)
+    * [pg8000.dbapi.DataError](#pg8000dbapidataerror)
+    * [pg8000.dbapi.OperationalError](#pg8000dbapioperationalerror)
+    * [pg8000.dbapi.IntegrityError](#pg8000dbapiintegrityerror)
+    * [pg8000.dbapi.InternalError](#pg8000dbapiinternalerror)
+    * [pg8000.dbapi.ProgrammingError](#pg8000dbapiprogrammingerror)
+    * [pg8000.dbapi.NotSupportedError](#pg8000dbapinotsupportederror)
+  * [Classes](#classes)
+    * [pg8000.dbapi.Connection](#pg8000dbapiconnection)
+    * [pg8000.dbapi.Connection.autocommit](#pg8000dbapiconnectionautocommit)
+    * [pg8000.dbapi.Connection.close()](#pg8000dbapiconnectionclose)
+    * [pg8000.dbapi.Connection.cursor()](#pg8000dbapiconnectioncursor)
+    * [pg8000.dbapi.Connection.rollback()](#pg8000dbapiconnectionrollback)
+    * [pg8000.dbapi.Connection.tpc\_begin(xid)](#pg8000dbapiconnectiontpc_beginxid)
+    * [pg8000.dbapi.Connection.tpc_commit(xid=None)](#pg8000dbapiconnectiontpc_commitxidnone)
+    * [pg8000.dbapi.Connection.tpc_prepare()](#pg8000dbapiconnectiontpc_prepare)
+    * [pg8000.dbapi.Connection.tpc_recover()](#pg8000dbapiconnectiontpc_recover)
+    * [pg8000.dbapi.Connection.tpc_rollback(xid=None)](#pg8000dbapiconnectiontpc_rollbackxidnone)
+    * [pg8000.dbapi.Connection.xid(format_id, global_transaction_id, branch_qualifier)](#pg8000dbapiconnectionxidformat_id-global_transaction_id-branch_qualifier)
+    * [pg8000.dbapi.Cursor](#pg8000dbapicursor)
+      * [pg8000.dbapi.Cursor.arraysize](#pg8000dbapicursorarraysize)
+      * [pg8000.dbapi.Cursor.connection](#pg8000dbapicursorconnection)
+      * [pg8000.dbapi.Cursor.rowcount](#pg8000dbapicursorrowcount)
+      * [pg8000.dbapi.Cursor.description](#pg8000dbapicursordescription)
+      * [pg8000.dbapi.Cursor.close()](#pg8000dbapicursorclose)
+      * [pg8000.dbapi.Cursor.execute(operation, args=None, stream=None)](#pg8000dbapicursorexecuteoperation-argsnone-streamnone)
+      * [pg8000.dbapi.Cursor.executemany(operation, param_sets)](#pg8000dbapicursorexecutemanyoperation-param_sets)
+      * [pg8000.dbapi.Cursor.callproc(procname, parameters=None)](#pg8000dbapicursorcallprocprocname-parametersnone)
+      * [pg8000.dbapi.Cursor.fetchall()](#pg8000dbapicursorfetchall)
+      * [pg8000.dbapi.Cursor.fetchmany(size=None)](#pg8000dbapicursorfetchmanysizenone)
+      * [pg8000.dbapi.Cursor.fetchone()](#pg8000dbapicursorfetchone)
+      * [pg8000.dbapi.Cursor.setinputsizes(\*sizes)](#pg8000dbapicursorsetinputsizessizes)
+      * [pg8000.dbapi.Cursor.setoutputsize(size, column=None)](#pg8000dbapicursorsetoutputsizesize-columnnone)
+    * [pg8000.dbapi.Interval](#pg8000dbapiinterval)
+* [Design Decisions](#design-decisions)
+* [Tests](#tests)
+* [Doing A Release Of pg8000](#doing-a-release-of-pg8000)
+* [Release Notes](#release-notes)
+  * [Version 1.31.5, 2025-09-14](#version-1315-2025-09-14)
+  * [Version 1.31.4, 2025-07-20](#version-1314-2025-07-20)
+  * [Version 1.31.3, 2025-07-19](#version-1313-2025-07-19)
+  * [Version 1.31.2, 2024-04-28](#version-1312-2024-04-28)
+  * [Version 1.31.1, 2024-04-01](#version-1311-2024-04-01)
+  * [Version 1.31.0, 2024-03-31](#version-1310-2024-03-31)
+  * [Version 1.30.5, 2024-02-22](#version-1305-2024-02-22)
+  * [Version 1.30.4, 2024-01-03](#version-1304-2024-01-03)
+  * [Version 1.30.3, 2023-10-31](#version-1303-2023-10-31)
+  * [Version 1.30.2, 2023-09-17](#version-1302-2023-09-17)
+  * [Version 1.30.1, 2023-07-29](#version-1301-2023-07-29)
+  * [Version 1.30.0, 2023-07-27](#version-1300-2023-07-27)
+  * [Version 1.29.8, 2023-06-16](#version-1298-2023-06-16)
+  * [Version 1.29.7, 2023-06-16](#version-1297-2023-06-16)
+  * [Version 1.29.6, 2023-05-29](#version-1296-2023-05-29)
+  * [Version 1.29.5, 2023-05-09](#version-1295-2023-05-09)
+  * [Version 1.29.4, 2022-12-14](#version-1294-2022-12-14)
+  * [Version 1.29.3, 2022-10-26](#version-1293-2022-10-26)
+  * [Version 1.29.2, 2022-10-09](#version-1292-2022-10-09)
+  * [Version 1.29.1, 2022-05-23](#version-1291-2022-05-23)
+  * [Version 1.29.0, 2022-05-21](#version-1290-2022-05-21)
+  * [Version 1.28.3, 2022-05-18](#version-1283-2022-05-18)
+  * [Version 1.28.2, 2022-05-17](#version-1282-2022-05-17)
+  * [Version 1.28.1, 2022-05-17](#version-1281-2022-05-17)
+  * [Version 1.28.0, 2022-05-17](#version-1280-2022-05-17)
+  * [Version 1.27.1, 2022-05-16](#version-1271-2022-05-16)
+  * [Version 1.27.0, 2022-05-16](#version-1270-2022-05-16)
+  * [Version 1.26.1, 2022-04-23](#version-1261-2022-04-23)
+  * [Version 1.26.0, 2022-04-18](#version-1260-2022-04-18)
+  * [Version 1.25.0, 2022-04-17](#version-1250-2022-04-17)
+  * [Version 1.24.2, 2022-04-15](#version-1242-2022-04-15)
+  * [Version 1.24.1, 2022-03-02](#version-1241-2022-03-02)
+  * [Version 1.24.0, 2022-02-06](#version-1240-2022-02-06)
+  * [Version 1.23.0, 2021-11-13](#version-1230-2021-11-13)
+  * [Version 1.22.1, 2021-11-10](#version-1221-2021-11-10)
+  * [Version 1.22.0, 2021-10-13](#version-1220-2021-10-13)
+
+<!-- mtoc-end -->
 
 ## Installation
 
@@ -110,7 +272,7 @@
 
 ```
 
-NB. There is [a longstanding bug](https://github.com/tlocke/pg8000/issues/36>) in the
+NB. There is [a longstanding bug](https://codeberg.org/tlocke/pg8000/issues/36) in the
 PostgreSQL server whereby if a `COMMIT` is issued against a failed transaction, the
 transaction is silently rolled back, rather than an error being returned. pg8000
 attempts to detect when this has happened and raise an `InterfaceError`.
@@ -2107,6 +2269,13 @@
 
 ## Release Notes
 
+### Version 1.31.5, 2025-09-14
+
+- Tiny performance improvement in reading from socket.
+- Fix bug in `literal()` where list is not properly escaped.
+- Move to [Codeberg](https://codeberg.org/tlocke/pg8000).
+
+
 ### Version 1.31.4, 2025-07-20
 
 - Various speed optimisations.
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/pyproject.toml /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/pyproject.toml
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -38,7 +38,7 @@
 dynamic = ["version"]
 
 [project.urls]
-Homepage = "https://github.com/tlocke/pg8000"
+Homepage = "https://codeberg.org/tlocke/pg8000"
 
 [tool.hatch.version]
 source = "versioningit"
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/src/pg8000/converters.py /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/src/pg8000/converters.py
--- a/src/pg8000/converters.py
+++ b/src/pg8000/converters.py
@@ -833,4 +833,4 @@
 
 @literal.register
 def _(value: list):
-    return f"'{array_out(value)}'"
+    return f"{literal(array_out(value))}"
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/src/pg8000/core.py /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/src/pg8000/core.py
--- a/src/pg8000/core.py
+++ b/src/pg8000/core.py
@@ -147,7 +147,7 @@
 
 
 def _read(sock, size):
-    buff = bytearray()
+    buff = bytearray(sock.read(size))
     try:
         while len(buff) < size:
             block = sock.read(size - len(buff))
diff -ruN --no-dereference /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.4/test/test_converters.py /home/ubuntu/closed-source-intelligence/data/cache/pypi-sdists/pg8000-1.31.5/test/test_converters.py
--- a/test/test_converters.py
+++ b/test/test_converters.py
@@ -358,7 +358,12 @@
 
 @pytest.mark.parametrize(
     "value,expected",
-    [("top_secret", "'top_secret'"), (["cove"], "'{cove}'")],
+    [
+        ("top_secret", "'top_secret'"),
+        (["cove"], "'{cove}'"),
+        (["'; SELECT 'Oops!' -- -"], """'{"''; SELECT ''Oops!'' -- -"}'"""),
+        ({"'; SELECT 'Oops!' -- -"}, """'{"''; SELECT ''Oops!'' -- -"}'"""),
+    ],
 )
 def test_literal(value, expected):
     assert literal(value) == expected