diff --git a/rules/S6466/impact.adoc b/rules/S6466/impact.adoc new file mode 100644 index 00000000000..ab7f007ac0b --- /dev/null +++ b/rules/S6466/impact.adoc @@ -0,0 +1,9 @@ +=== What is the potential impact? + +Issues of this type interrupt the normal execution of a program, causing it to +crash or putting it into an inconsistent state. +Therefore, this issue might impact the availability and reliability of your +application, or even result in data loss. + +If the computation of an index value is tied to user input data, this issue can +potentially even be exploited by attackers to disrupt your application. diff --git a/rules/S6466/java/rule.adoc b/rules/S6466/java/rule.adoc index bb3fcee92ac..5308c101cea 100644 --- a/rules/S6466/java/rule.adoc +++ b/rules/S6466/java/rule.adoc @@ -1,58 +1,136 @@ -An array index out of bounds exception is a bug class that occurs in Java when a program tries to access an array element that does not exist. This bug can cause a Java program to crash or behave unexpectedly, and it can also lead to security vulnerabilities. To fix an array index out of bounds exception in Java, you should always ensure that you are accessing array elements within the bounds of the array. +An array index out-of-bounds exception is a bug class that occurs in Java when a +program tries to access an array element that does not exist. +This bug can cause your program to crash or behave unexpectedly. +To fix an array index out of bounds exception in Java, you should always ensure +that you are accessing array elements within the bounds of the array. -// If you want to factorize the description uncomment the following line and create the file. -//include::../description.adoc[] == Why is this an issue? -Array index out of bounds exception is the indication of a bug or a logical error in the code. It occurs when you try to access an array element with an index that is outside the valid range of the array. When it happens, it can cause the program to crash or put it into an inconsistent state. This can lead to serious consequences such as data loss. When the program is used for critical tasks such as financial transactions or medical records, the consequences could even be more severe. Therefore, it is important to fix array index out of bounds exception in Java to ensure that programs are reliable, secure, and function as intended. -//=== What is the potential impact? +An array index out-of-bounds exception indicates a bug or a logical error in the +code. -== How to fix it -//== How to fix it in FRAMEWORK NAME - -To fix array index out of bounds exception in Java, you should always ensure that you are accessing array elements within the bounds of the array. Here are some best practices to follow: +In Java, arrays have a fixed size, and their elements are indexed starting from +`0`, counting up to the last index that is still smaller than the size. +When trying to access an array outside of this range, an +`ArrayIndexOutOfBoundsException` will be thrown and the operation will fail. +include::../impact.adoc[] -Always check the length of the array before accessing elements. This can be done using the `length` property of the array. For example, `myArray.length` returns the length of the `myArray` array. Also ensure that you are not accessing an array element located before position zero. This will ensure that you do not try to access elements that do not exist. +== How to fix it -Use loops to iterate over arrays instead of accessing elements directly. This can be done using the `for` loop or the `foreach` loop. For example, `for (int i = 0; i < myArray.length; i++)` iterates over the `myArray` array using an index variable `i`. The same goes for `for (Object o : myArray)`. If, inside the loop, you manipulate the index used to access the array, then make sure that the resulting index stays within the array bound. This will ensure that you do not accidentally access elements that do not exist. +=== Code examples +The following examples contain out-of-bounds accesses to arrays, resulting +in `ArrayIndexOutOfBounds` exceptions. +These situations can be avoided by carefully considering the range of valid +index values, or even better, by comparing indices against the size of a +sequence. -=== Code examples +In this first example, the array `arr` contains three elements. +Since the first element of a list has index `0`, the last valid index is `2`. +Therefore, an `ArrayIndexOutOfBoundsException` is thrown when accessing `arr` at +index `3`. ==== Noncompliant code example -[source,text,diff-id=1,diff-type=noncompliant] +[source,java,diff-id=1,diff-type=noncompliant] ---- - void nonCompliant() { - int[] l = {1, 2, 3}; + void fun() { + int[] arr = {1, 2, 3}; - int x = l[-1]; // Noncompliant - int y = l[3]; // Noncompliant + int x = arr[3]; // Noncompliant: Valid indices are 0, 1 and 2 } ---- ==== Compliant solution -[source,text,diff-id=1,diff-type=compliant] +[source,java,diff-id=1,diff-type=compliant] +---- + void fun() { + int[] arr = {1, 2, 3}; + + int x = arr[0]; + } ---- - void compliant() { - int[] l = {1, 2, 3}; - int x = l[0]; - int y = l[2]; +Accessing an array with its size as the index is never correct: + +==== Noncompliant code example + +[source,java,diff-id=2,diff-type=noncompliant] +---- + void fun(int[] arr) { + System.out.println(arr[arr.length]); // Noncompliant: Indexing starts at 0, hence array.length will always be an invalid index. } ---- +==== Compliant solution + +[source,java,diff-id=2,diff-type=compliant] +---- + void compliant(int[] arr) { + // We can make sure arr is non-empty before trying to access its last element. + if (arr.length > 0) { + System.out.println(arr[arr.length - 1]); + } else { + System.out.println("Empty array!"); + } + } +---- + +=== How does this work? + +You should always ensure that you are accessing arrays using indices that are +within the bounds of the array. +Here are some best practices to follow: + +Always compare indices against the length of an array using `if-else` statements +or other control flow constructs before accessing elements. +For this, you can use the `length` property of arrays. +For example, `arr.length` returns the length of the `arr` array. +Also, ensure that you are not accessing an array using negative indices. + +Use loops to iterate over arrays instead of accessing elements directly. +This can be done using the `for` loop or the `foreach` loop. +For example, `for (int i = 0; i < arr.length; ++i)` iterates over the `arr` +array within its bounds using an index variable `i`. +The same goes for `for (Object o : arr)`. +If you manipulate the index used to access an array inside the loop, make sure +that the resulting index stays within bounds. + +=== Pitfalls + +The indices `0`, or `arr.length - 1` for the first and last element of an array +are not always valid! +Make sure the array in question is not empty before accessing these indices. + +=== Going the extra mile + +In many cases you can eliminate loops containing index-based accesses altogether +by applying the Java stream API to arrays using `java.util.Arrays.stream()`. +The API provides methods like `map()` or `filter()` that allow you to transform, +filter and perform many different operations on the elements of an array without +using indices. + + == Resources === Documentation -* Java Documentation - https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ArrayIndexOutOfBoundsException.html[ArrayIndexOutOfBoundsException] +* https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ArrayIndexOutOfBoundsException.html[ArrayIndexOutOfBoundsException] +* https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Arrays.html#stream(T%5B%5D)[Javadoc for `Arrays.stream()`] +* https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/stream/Stream.html[Stream API] + +=== Articles & blog posts + * A Reference Guide - https://www.baeldung.com/java-arrays-guide[Arrays in Java] +=== Standards + +* https://docs.oracle.com/javase/specs/jls/se17/html/jls-10.html#jls-10.4[Array Access Specification] + ifdef::env-github,rspecator-view[] diff --git a/rules/S6466/python/metadata.json b/rules/S6466/python/metadata.json index 7c3c900c90b..5f2aee6ceb8 100644 --- a/rules/S6466/python/metadata.json +++ b/rules/S6466/python/metadata.json @@ -1,5 +1,5 @@ { - "title": "Accessing list elements should not trigger an IndexError", + "title": "Accessing sequence elements should not trigger an IndexError", "type": "BUG", "code": { "impacts": { diff --git a/rules/S6466/python/rule.adoc b/rules/S6466/python/rule.adoc index 419d58f0c4a..c0be383a677 100644 --- a/rules/S6466/python/rule.adoc +++ b/rules/S6466/python/rule.adoc @@ -1,10 +1,41 @@ -This rule raises an issue when trying to access a list or a tuple index that is out of bounds. + +An `IndexError` is a bug class that occurs in Python when a program tries to +access a list, a tuple or other types of sequences at an index that does not +exist. +This bug can cause your program to crash or behave unexpectedly. +To fix an `IndexError`, you should always ensure you are accessing sequences +within their bounds. == Why is this an issue? -Trying to access a list or a tuple index that is beyond the size of the list/tuple is probably a mistake and will result in an `IndexError`. +An `IndexError` indicates a bug or a logical error in the code. + +In Python, lists and tuples have a certain length and their elements are indexed +starting from `0`, counting up to the last index that is still smaller than the +length. +When trying to access a list or tuple with an index outside of this range, +an `IndexError` will be raised and the operation will fail. + +Negative indices are supported. When using a negative index, it will be +interpreted by computing the sum of the negative index and the list size. +The result is then used as the actual index for accessing the sequence. +Thus, this sum must be non-negative and fit into the aforementioned range. + +include::../impact.adoc[] + +== How to fix it -=== Code examples +=== Code examples + +The following examples contain out-of-bounds accesses to sequences, resulting in +an `IndexError`. +These situations can be avoided by carefully considering the range of valid +index values, or even better, by comparing indices against the length of a +sequence. + +In this first example, the list `ls` contains three elements. +Since the first element of a list has index `0`, the last valid index is `2`. +Therefore, an `IndexError` is raised when accessing `ls` at index `3`. ==== Noncompliant code example @@ -26,11 +57,85 @@ def fun(): ---- +Accessing a list with its length as the index is never correct: + +==== Noncompliant code example + +[source,python,diff-id=2,diff-type=noncompliant] +---- +def fun(ls: list[int]): + print(ls[len(ls)]) # Noncompliant: Indexing starts at 0, hence the list length will always be an invalid index. + +---- + +==== Compliant solution + +[source,python,diff-id=2,diff-type=compliant] +---- +def fun(ls: list[int]): + # We can make sure ls is non-empty before trying to access its last element. + # Also, the index `len(ls) - 1` or just `-1` will correctly select the last + # element within bounds. + print("Empty list!" if not ls else ls[-1]) + +---- + +=== How does this work? + +You should always ensure that you are accessing sequences using indices that are +within the bounds of the sequence. +Here are some best practices to follow: + +Always compare indices against the length of a sequence using `if-else` +statements or other control flow constructs before accessing elements. +The length can be retrieved using the built-in `len(...)` function. +For example, `len(ls)` returns the length of the list `ls`. + +Use loops to iterate over sequences instead of accessing elements directly. +For example, `for elem in ls:` iterates over the list `ls`. + +=== Pitfalls + +The indices `0`, `len(...) - 1`, or `-1` for the first and last element of a +sequence are not always valid! +Make sure the sequence in question is non-empty before accessing these +indices. + +=== Going the extra mile + +Built-in functions like `map()`, `filter()`, and `reduce()` present additional +ways to operate on sequences without using indices. + +If you absolutely need to know the index of an element while iterating over a +sequence, you can use `enumerate()`. It provides you the indices and the +elements of a sequence during iteration, eliminating the need to manually +retrieve elements from the sequence using indices. + +==== Noncompliant code example + +[source,python,diff-id=3,diff-type=noncompliant] +---- +for i in range(len(ls)): + elem = ls[i] # We can eliminate this access by index using enumerate. + + foo(i, elem) +---- + +==== Compliant solution + +[source,python,diff-id=3,diff-type=compliant] +---- +for i, elem in enumerate(ls): + foo(i, elem) +---- + == Resources === Documentation -* Python Documentation - https://docs.python.org/3/library/exceptions.html#IndexError[IndexError] +* https://docs.python.org/3/reference/expressions.html#subscriptions[Subscriptions] +* https://docs.python.org/3/library/exceptions.html#IndexError[IndexError] +* https://docs.python.org/3/library/functions.html#built-in-functions[Built-ins, including `map`, `filter`, `enumerate`, etc.] ifdef::env-github,rspecator-view[]