Skip to content

feat: add post-quantum cryptography (PQC) integration tests and fixtures#17586

Draft
ohmayr wants to merge 1 commit into
mainfrom
feat-pqc-verification
Draft

feat: add post-quantum cryptography (PQC) integration tests and fixtures#17586
ohmayr wants to merge 1 commit into
mainfrom
feat-pqc-verification

Conversation

@ohmayr

@ohmayr ohmayr commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Test

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for mTLS in REST transport fixtures within conftest.py and introduces a new integration test in test_pqc.py to verify Post-Quantum Cryptography (PQC) negotiation with the Showcase server. The review feedback highlights several critical improvements: resolving a compatibility issue with urllib3 v2.0+ by overriding cert_verify instead of using assert_hostname in PoolManager, adding defensive None checks for gRPC metadata and interceptor attributes to prevent runtime errors, and avoiding shadowing the built-in dir function.

Comment on lines +458 to +467
class HostNameIgnoringAdapter(HTTPAdapter):
"""Custom HTTPAdapter that disables hostname verification for local self-signed certs."""
def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs):
self.poolmanager = PoolManager(
num_pools=connections,
maxsize=maxsize,
block=block,
assert_hostname=False,
**pool_kwargs
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In urllib3 v2.0+, the assert_hostname parameter has been removed from PoolManager, which will cause a TypeError at runtime. Instead of overriding init_poolmanager, you can override cert_verify on the HTTPAdapter to disable hostname verification in a way that is compatible with both urllib3 1.x and 2.x.

class HostNameIgnoringAdapter(HTTPAdapter):
    """Custom HTTPAdapter that disables hostname verification for local self-signed certs."""
    def cert_verify(self, conn, url, verify, cert):
        super().cert_verify(conn, url, verify, cert)
        conn.assert_hostname = False

Comment on lines +21 to +22
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If the suggestion to use cert_verify in HostNameIgnoringAdapter is applied, PoolManager is no longer needed and its import can be removed.

Suggested change
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager
from requests.adapters import HTTPAdapter

self._add_request_metadata(client_call_details)
response = continuation(client_call_details, request)
metadata = [(k, str(v)) for k, v in response.trailing_metadata()]
metadata = [(k, str(v)) for k, v in response.initial_metadata()] + [(k, str(v)) for k, v in response.trailing_metadata()]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In gRPC Python, initial_metadata() and trailing_metadata() can return None in certain circumstances (e.g., if the call failed or in mock/test environments). To prevent a TypeError when iterating or concatenating, add defensive None checks.

Suggested change
metadata = [(k, str(v)) for k, v in response.initial_metadata()] + [(k, str(v)) for k, v in response.trailing_metadata()]
initial_metadata = response.initial_metadata() or []
trailing_metadata = response.trailing_metadata() or []
metadata = [(k, str(v)) for k, v in initial_metadata] + [(k, str(v)) for k, v in trailing_metadata]
References
  1. Specifically enforce defensive programming: for languages that support nullable references (e.g., Go, Python, Java), ensure appropriate null/nil/None checks or other language-idiomatic guards exist before object property accesses.

Comment on lines +484 to +486
dir = os.path.dirname(__file__)
cert_path = os.path.join(dir, "../cert/mtls.crt")
key_path = os.path.join(dir, "../cert/mtls.key")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using dir as a variable name because it shadows the Python built-in dir() function. Rename it to something more descriptive like current_dir or base_dir.

Suggested change
dir = os.path.dirname(__file__)
cert_path = os.path.join(dir, "../cert/mtls.crt")
key_path = os.path.join(dir, "../cert/mtls.key")
current_dir = os.path.dirname(__file__)
cert_path = os.path.join(current_dir, "../cert/mtls.crt")
key_path = os.path.join(current_dir, "../cert/mtls.key")

Comment on lines +511 to +513
dir = os.path.dirname(__file__)
cert_path = os.path.join(dir, "../cert/mtls.crt")
key_path = os.path.join(dir, "../cert/mtls.key")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Avoid using dir as a variable name because it shadows the Python built-in dir() function. Rename it to something more descriptive like current_dir or base_dir.

Suggested change
dir = os.path.dirname(__file__)
cert_path = os.path.join(dir, "../cert/mtls.crt")
key_path = os.path.join(dir, "../cert/mtls.key")
current_dir = os.path.dirname(__file__)
cert_path = os.path.join(current_dir, "../cert/mtls.crt")
key_path = os.path.join(current_dir, "../cert/mtls.key")

Comment on lines +38 to +42
for key, value in interceptor.response_metadata:
if key.lower() == "x-showcase-tls-group":
negotiated_group = value
elif key.lower() == "x-showcase-tls-client-supported-groups":
supported_groups = value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing interceptor.response_metadata directly can raise an AttributeError or TypeError if the attribute is not set or is None (e.g., if the call failed or if the interceptor does not support it). Use getattr with a default value to safely access it.

Suggested change
for key, value in interceptor.response_metadata:
if key.lower() == "x-showcase-tls-group":
negotiated_group = value
elif key.lower() == "x-showcase-tls-client-supported-groups":
supported_groups = value
response_metadata = getattr(interceptor, "response_metadata", None) or []
for key, value in response_metadata:
if key.lower() == "x-showcase-tls-group":
negotiated_group = value
elif key.lower() == "x-showcase-tls-client-supported-groups":
supported_groups = value
References
  1. Specifically enforce defensive programming: for languages that support nullable references (e.g., Go, Python, Java), ensure appropriate null/nil/None checks or other language-idiomatic guards exist before object property accesses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant