Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6bfacd7
test: expose TLS configuration subclass dispatch
seonghobae Aug 11, 2026
d0567c4
fix: reject TLS configuration subclasses before dispatch
seonghobae Aug 11, 2026
39409c6
Merge remote-tracking branch 'origin/main' into codex/reconstruct-tls…
seonghobae Aug 11, 2026
b193dc5
test: require TLS exact-type documentation
seonghobae Aug 11, 2026
311e07e
docs: explain exact TLS configuration boundary
seonghobae Aug 11, 2026
4b07c81
docs: record exact TLS configuration boundary
seonghobae Aug 11, 2026
fbe041b
Merge remote TLS documentation update
seonghobae Aug 11, 2026
72d25e5
docs: record exact TLS configuration security change
seonghobae Aug 11, 2026
9a16f26
Merge remote-tracking branch 'origin/security/reconstruct-exact-tls-8…
seonghobae Aug 11, 2026
77e6151
Merge branch 'main' into security/reconstruct-exact-tls-88c81a
opencode-agent[bot] Aug 11, 2026
2e647ea
Merge remote-tracking branch 'origin/main' into codex/reconstruct-tls…
seonghobae Aug 11, 2026
5769af0
Merge remote-tracking branch 'origin/security/reconstruct-exact-tls-8…
seonghobae Aug 11, 2026
cc7508d
test: bind TLS boundary documentation wording
seonghobae Aug 11, 2026
16cc104
merge: update TLS boundary on current main
seonghobae Aug 11, 2026
500e960
fix: keep TLS fixture lint-clean
seonghobae Aug 11, 2026
30296a3
Merge branch 'main' of https://github.com/ContextualWisdomLab/EgressW…
seonghobae Aug 11, 2026
2974c7d
Merge branch 'main' into security/reconstruct-exact-tls-88c81a
opencode-agent[bot] Aug 11, 2026
2ee45ef
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 11, 2026
3461999
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 11, 2026
f260402
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 11, 2026
f1da8a7
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 11, 2026
3b87e14
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 11, 2026
ebc0db1
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae Aug 12, 2026
ec15fb7
merge: refresh TLS configuration on current main
seonghobae Aug 12, 2026
3114545
Merge current protected main into TLS policy branch
seonghobae Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Add immutable provider-neutral `TLSConfiguration` dependency injection for
private trust stores and mutual-TLS client identities across synchronous and
asynchronous DNS-pinned builders. TLS 1.3 is the default; explicit TLS 1.2
compatibility remains restricted to forward-secret ECDHE suites.
compatibility remains restricted to forward-secret ECDHE suites. Before
dispatch, builders require the exact `TLSConfiguration` type so a subclass
cannot replace context construction or inject subclass-controlled TLS policy.
- Add explicit, deterministic `EgressDecisionEvidence` for successful egress
decisions. Evidence revalidates signed state and records canonical authority,
method policy, aggregate address-family counts, and correlation fingerprints
Expand Down Expand Up @@ -86,6 +88,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
disable the recurring loop.

### Security
- Require the exact `TLSConfiguration` type before TLS context creation. A
subclass can no longer override `create_ssl_context()` to replace the reviewed
immutable policy with a context that disables hostname or certificate
verification; private trust, mTLS, and explicit TLS 1.2 compatibility remain
available through the documented declarative fields.
- Reject non-exact integer subclasses in shared policy integer fields before
retaining trusted configuration state. Exact built-in integers and existing
ASCII decimal strings remain supported, preserving defaults and ranges while
Expand Down
18 changes: 18 additions & 0 deletions docs/research/tls-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ trusted integration point for deferred secret retrieval. Every transport owns
the fresh context that results, eliminating post-validation caller mutation as
an authority channel.

The public context helper accepts only the exact `TLSConfiguration` type before
it invokes `create_ssl_context()`. Subclassing this security value object is not
an extension mechanism: a subclass could otherwise replace
`create_ssl_context()` with caller-controlled dispatch and return a context that
disables hostname verification or certificate verification while still passing
an `isinstance` check. Rejecting subclasses before that method is invoked keeps
the reviewed configuration fields, not polymorphic code, authoritative for TLS
policy. This is a pre-1.0 secure-default tightening and does not claim to sandbox
arbitrary trusted Python executing inside the embedding process.

## Trust-store semantics

The default preserves EgressWeave's existing HTTPX trust behavior while
Expand Down Expand Up @@ -103,6 +113,11 @@ default. An existing endpoint that cannot yet negotiate TLS 1.3 can opt into
`minimum_version=ssl.TLSVersion.TLSv1_2`; this is an explicit compatibility
exception that should be inventoried and removed after the peer is upgraded.

Applications that previously subclassed `TLSConfiguration` must migrate to an
exact instance using the documented declarative fields. Private trust roots,
mutual-TLS identities, deferred private-key passwords, and the explicit TLS 1.2
compatibility floor remain supported without subclassing.

The configuration is threaded through both public builders and both
already-validated pinned-client builders. It changes only TLS trust and client
identity; exact authority, DNS pinning, proxy isolation, request framing and
Expand All @@ -114,6 +129,9 @@ independently enforced.
Aviram, N. (2026). *Deprecating obsolete key exchange methods in TLS 1.2 and
DTLS 1.2* (RFC 10015). RFC Editor. https://www.rfc-editor.org/rfc/rfc10015.html

MITRE Corporation. (2026). *CWE-295: Improper certificate validation* (CWE
Version 4.20). https://cwe.mitre.org/data/definitions/295.html

OpenSSL Project Authors. (2026). *openssl-ciphers*. OpenSSL 3.0 documentation.
https://docs.openssl.org/3.0/man1/openssl-ciphers/

Expand Down
2 changes: 1 addition & 1 deletion src/egressweave/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def create_egress_ssl_context(
"""Create the default HTTPX context or a fresh configured enterprise context."""
if configuration is None:
return _create_httpx_ssl_context(verify=True, trust_env=False)
if not isinstance(configuration, TLSConfiguration):
if type(configuration) is not TLSConfiguration:
raise TypeError("tls_configuration must be TLSConfiguration or None")
return configuration.create_ssl_context()

Expand Down
25 changes: 25 additions & 0 deletions tests/test_tls_configuration_exact_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Exact-type security contracts for enterprise TLS configuration."""

from __future__ import annotations

import ssl

import pytest

from egressweave.tls import TLSConfiguration, create_egress_ssl_context


class _VerificationDisablingTLSConfiguration(TLSConfiguration):
"""Fail if subclass-controlled TLS context dispatch occurs."""

def create_ssl_context(self) -> ssl.SSLContext:
"""Reject any attempt to execute subclass-controlled TLS policy code."""
raise AssertionError("subclass dispatch must not occur")


def test_context_helper_rejects_tls_configuration_subclasses_before_dispatch() -> None:
"""Reject polymorphic configuration before subclass code can replace TLS policy."""
configuration = _VerificationDisablingTLSConfiguration()

with pytest.raises(TypeError, match="TLSConfiguration or None"):
create_egress_ssl_context(configuration)
43 changes: 43 additions & 0 deletions tests/test_tls_configuration_exact_type_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Documentation contracts for the exact TLS configuration type boundary."""

from __future__ import annotations

from pathlib import Path

REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
TLS_GUIDE = REPOSITORY_ROOT / "docs" / "research" / "tls-configuration.md"
CHANGELOG = REPOSITORY_ROOT / "CHANGELOG.md"


def _normalized(path: Path) -> str:
"""Return repository documentation with insignificant whitespace collapsed."""
return " ".join(path.read_text(encoding="utf-8").split())


def test_tls_guide_requires_exact_configuration_type_before_dispatch() -> None:
"""Explain why subclass dispatch cannot replace the reviewed TLS policy."""
guide = _normalized(TLS_GUIDE)

assert (
"The public context helper accepts only the exact `TLSConfiguration` type "
"before it invokes `create_ssl_context()`."
) in guide
assert (
"a subclass could otherwise replace `create_ssl_context()` with "
"caller-controlled dispatch and return a context that disables hostname "
"verification or certificate verification"
) in guide


def test_changelog_records_exact_tls_configuration_type_boundary() -> None:
"""Keep the pre-1.0 TLS policy-integrity tightening visible to integrators."""
changelog = _normalized(CHANGELOG)

assert (
"Require the exact `TLSConfiguration` type before TLS context creation."
) in changelog
assert (
"A subclass can no longer override `create_ssl_context()` to replace the "
"reviewed immutable policy with a context that disables hostname or "
"certificate verification"
) in changelog
Loading