
Accessing attributes on `None` is almost always a logical error and will raise
an `AttributeError`.
To fix this issue, make sure not to access an attribute or method on a value
that can be `None`.

== Why is this an issue?

`None` is a built-in object that represents the absence of a value.
It is often used as a placeholder value for variables that only sometimes hold a
value or as a return value for method calls that have no result.

Attributes and methods of symbols that sometimes can be `None` should only be
accessed in circumstances where it is certain that they are not set to `None`.
Otherwise, an `AttributeError` is raised, and the program is interrupted.
Hence, this issue indicates a logical error as it results from incorrect
assumptions about the state of variables or the results of computations.

=== What is the potential impact?

include::../../../shared_content/layc/exception-impact.adoc[]

If a `None` value can be induced by user input, this issue may even be
exploited by attackers to disrupt your application or gain information from
stack traces.

=== Exceptions

`None` does support a fixed set of special attributes like `++__class__++` or
`++__bool__++`, and this issue is not raised when accessing these attributes.

== How to fix it

If your code contains `if-else` statements or similar constructs where some
branches potentially assign the `None` value to a variable, you must ensure
that this variable is handled safely afterwards.
I.e., its attributes should not be accessed at all or only after explicitly
confirming that it is not `None`.

Similarly, for any function calls that can return `None` under certain
conditions, carefully confirm that your code avoids these conditions.
Again, the safest approach is to check for a `None` return value explicitly.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
def render(file_path):
    if os.path.isfile(file_path):
        data = interpret_csv(file_path)
    else:
        data = None

    # ...

    data.plot_graph() # Noncompliant
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
def render(file_path):
    if os.path.isfile(file_path):
        data = interpret_csv(file_path)
    else:
        data = None

    # ...

    if data is not None:
        data.plot_graph()
    else:
        print("No data available.")
----
        
=== How does this work?

In the given example, the function `render` tries to load information into a
variable `data`, depending on whether `file_path` is a path to a file.
If this is not the case, `None` is assigned to `data`.

At the end of the function, a method `plot_graph()` is called on `data`.
The call is sure to fail if `data` was assigned to `None`.
This is prevented by checking first whether `data` is not `None` before
performing the call.

== Resources

=== Documentation

* The Python Data Model on https://docs.python.org/3/reference/datamodel.html#none[None]
* https://docs.python.org/3/library/exceptions.html#AttributeError[Attribute Error]
* https://docs.python.org/3/reference/expressions.html#attribute-references[Attribute References]

=== Articles & blog posts

* CVE  - https://cwe.mitre.org/data/definitions/476[CWE-476 - NULL Pointer Dereference]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

include::../message.adoc[]

include::../highlighting.adoc[]

'''
== Comments And Links
(visible only on this page)

include::../comments-and-links.adoc[]

endif::env-github,rspecator-view[]
