Skip to content

Bug: Shared Mutable Class-Level context Dict in Confidence #114

@ausbernard

Description

@ausbernard

👋 👋
Severity: Critical
File: confidence/confidence.py:80

I forked the repository and made a branch but wanted to respect the contribution guidelines before I opened a pull request.


description

context is declared as a class-level attribute with a mutable default dict:

class Confidence:
     context: Dict[str, FieldType] = {}

because__init__never assigns self.context = {}, all instances share the same dict object at the class level. Any call to put_context on one instance mutates that shared dict, silently contaminating every other Confidence instance in the process.

impact

  • context set on one Confidence instance leaks into all other instances.
  • child instances created via with_context are safe (they assign a new dict), but the root instance and any sibling roots
    are not.
  • in a multi-tenant or multi-user scenario, this is a data isolation failure.

reproduction

from confidence.confidence import Confidence

a = Confidence("secret-a")
b = Confidence("secret-b")

a.put_context("user", "alice")
print(b.context)  # {"user": "alice"} — wrong, b is contaminated

Bug confirmed. b was never touched, but it has Alice's context because both instances share the same class-level dict.

fix

  1. context (critical)
    add self.context: Dict[str, FieldType] = {} inside __init__, and change the async_client default to None with a guard in the body.

  2. async_client (secondary) Change the default from httpx.AsyncClient() (evaluated once at class definition time, shared across all instances) to None, instantiating a fresh httpx.AsyncClient() per instance in the body of __init__. This prevents shared client state under concurrent use.

  3. Regression test added tests/test_confidence.py::TestConfidence::test_context_is_isolated_per_instance — creates two root instances, mutates one, and asserts the other is unaffected.


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions