Testers find Python Class Pollution by looking for code paths that let attacker-controlled data modify class attributes or shared objects at runtime. Review deserialisation, template, and object-merge logic, then attempt to change behaviour across instances. Check whether polluted attributes affect authorisation, logging, or other security-sensitive decisions.
How testers approach Python class pollution
Testing for python class pollution starts with finding places where untrusted input can reach shared class state, not just instance fields. The main targets are deserialisation, templating, object-merging, and any helper that writes attributes dynamically. A useful test asks whether attacker-controlled data can change behaviour for later objects, or whether a polluted attribute changes a security decision.
Look for patterns such as setattr-style updates, recursive merges, or code that copies user input into nested objects without strict allowlists. The question is not only whether the payload changes one object, but whether it changes the class itself, prototype-like shared state, or any reusable default that other instances inherit.
In practice, testers should compare normal object behaviour before and after the input path is exercised, then observe whether the altered state survives across instances. That includes changes to logging fields, authorisation logic, feature flags, or error handling. A finding becomes much stronger when the polluted attribute affects a control path rather than a cosmetic property.
Where the bug usually hides in Python code
Python class pollution is usually exposed by code that treats structured input as trusted object data. Deserialisation wrappers, template helpers, JSON-to-object adapters, and convenience merge functions are common entry points because they often prioritise flexibility over strict field control. Testers should trace how a payload is transformed, not just where it is stored.
The key failure mode is accidental writes to shared class attributes or to nested objects that are reused as defaults. If a test can change one object and the next object inherits that change, the application has crossed from per-object mutation into shared-state pollution. That is the behaviour to prove, because it is what turns a parsing issue into a security issue.
Review the surrounding code for assumptions that only instance data can be changed. In Python, dynamic attribute access and mutable defaults make that assumption unsafe unless the developer explicitly constrains the write path. The most productive tests focus on whether the code trusts keys, paths, or attribute names from user input and then applies them without validation.
How to prove impact, not just mutation
A good test does more than show that an attribute changed. It should demonstrate an observable consequence that matters to the application. The strongest cases are where polluted state alters authorisation, logging, routing, request handling, or other branch decisions that affect trust. If the behaviour changes only in memory and nowhere else, the issue may still be real, but it is less useful to a defender.
Testers should also check whether the polluted state can be chained with another flaw, such as unsafe deserialisation or weak template evaluation. That combination can turn a local mutation into broader code influence. When the polluted field is read later by a security-sensitive function, the test has enough evidence to support remediation priority.
The most reliable proofs use repeatable inputs and a clear before-and-after comparison. If the application logs one identity before pollution and another after, or if it skips an access check after the shared attribute changes, the impact is concrete. That is the difference between a suspicious parsing bug and a security-relevant class pollution finding.
Risk and Threat Considerations
Class pollution matters because shared runtime state can influence more than the original object that was attacked. If attackers can steer class attributes, they may alter security decisions, suppress logs, or widen the effect of a single request across later objects that reuse the same class state.
Failure mechanism: A code path accepts attacker-controlled keys or nested objects and writes them into shared class-level or reusable state, then later logic reads that polluted state as if it were trusted application data.
Impact: The result can be privilege abuse, audit evasion, corrupted business logic, or broader compromise of application behaviour across requests or instances.
Standards & Framework Alignment
This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.
OWASP API Security Top 10 addresses the attack and risk surface, while OWASP ASVS, NIST SP 800-53 Rev 5 and CIS Controls v8 set the governance and control requirements practitioners need to meet.
| Framework | Control / Reference | Relevance |
|---|---|---|
| OWASP ASVS | V15 — Secure Coding and Architecture | Class pollution is a code-structure flaw in unsafe object handling. |
| Recommendation — Review object update paths for unsafe attribute writes and constrain shared-state mutation. | ||
| NIST SP 800-53 Rev 5 | SI-10 — Information Input Validation | Untrusted input drives the attribute and merge paths that enable pollution. |
| Recommendation — Validate and constrain input keys before they can update program state. | ||
| OWASP API Security Top 10 | API8 — Security Misconfiguration | Dynamic object population often reflects weak defensive configuration and trust assumptions. |
| Recommendation — Harden request-to-object mapping so attacker input cannot alter shared application state. | ||
| CIS Controls v8 | CIS-16 — Application Software Security | Testing class pollution is part of secure application design and verification. |
| Recommendation — Test application update logic for unsafe object mutation and fix the trusted write path. | ||
Practitioner Guidance
What to verify: Confirm whether the write path can reach class attributes, default objects, or other shared state, then verify whether the changed value survives into a later decision point. If you only see a one-off mutation with no later read, the priority is lower than a case that changes authorisation or logging.
Decision rule: If the polluted attribute feeds a security control, treat the issue as high priority even if the initial payload seems harmless. If the attribute only affects display or convenience behaviour, focus remediation on the unsafe merge or attribute-write pattern, but do not overstate the security impact.
Practitioner takeaway: The important question is not whether Python lets the attribute change, it is whether attacker-controlled state can cross object boundaries and influence a later trust decision.
Related resources from NHI Mgmt Group
- Guide: Secure Coding Guide
- Python Class Pollution
- Why do general-purpose LLMs miss vulnerabilities that human testers still find?
- How should security teams use taint analysis to find injection flaws in Python applications without drowning in false positives?
- Why do autonomous testers find IDORs that simple scanners miss?