Skip to content

Commit

Permalink
Modify rule S6466: Update Java rspec
Browse files Browse the repository at this point in the history
  • Loading branch information
anton-haubner-sonarsource committed Sep 26, 2023
1 parent 111d418 commit 227e4cb
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 63 deletions.
12 changes: 12 additions & 0 deletions rules/S6466/impact.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
=== 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 result in data loss.
When the program is used for critical tasks such as financial transactions or
medical records, the consequences could be severe.

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.
127 changes: 101 additions & 26 deletions rules/S6466/java/rule.adoc
Original file line number Diff line number Diff line change
@@ -1,56 +1,131 @@

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 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.

// 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
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.

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:
include::../impact.adoc[]

== How can I fix it?

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.
The following are examples of code containing 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 and the size of a sequence.

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.
In this first example an array `arr` containing three elements is being created.
Since in Java, 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

=== Code examples

==== 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};
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
int y = arr[-1]; // Noncompliant: Negative indices are not supported in Java
}
----

==== Compliant solution
=== Compliant solution

[source,text,diff-id=1,diff-type=compliant]
[source,java,diff-id=1,diff-type=compliant]
----
void compliant() {
int[] l = {1, 2, 3};
int[] arr = {1, 2, 3};
int x = arr[0];
int y = arr[2];
}
----

The following example is similar, but we don't know the concrete length of the
array `arr` so we use `array.length` instead.
Still, accessing an array with its size as an index is not correct:

=== Noncompliant code example

[source,java,diff-id=2,diff-type=noncompliant]
----
void nonCompliant(int[] arr) {
System.out.println(arr[arr.length]); # Noncompliant: Indexing starts at 0, hence array.length will always be an invalid index.
}
----

=== Compliant solution

int x = l[0];
int y = l[2];
[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 (array.length > 0) {
System.out.println(arr[arr.length - 1]);
} else {
System.out.println("Empty array!");
}
}
----

== Resources
=== How does it 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 and the length of an array before accessing elements
using `if-else` constructs or other control flow tools.
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 < myArray.length; i++)` iterates over the `myArray` array using an index variable `i` within its bounds.
The same goes for `for (Object o : myArray)`.
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 a
sequence are not always valid!
Make sure the array in question is non-empty before accessing these indices.

=== Going the extra mile

In many cases you can eliminate loops containing index 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.

== More Info

=== Resources

==== Documentation

* https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ArrayIndexOutOfBoundsException.html[ArrayIndexOutOfBoundsException]
* https://docs.oracle.com/javase/specs/jls/se7/html/jls-10.html#jls-10.4[Array Access Specification]
* https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#stream-T:A-[`Arrays.stream()`]
* https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html[Stream API]

=== Documentation
==== Articles & blog posts

* Java Documentation - https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ArrayIndexOutOfBoundsException.html[ArrayIndexOutOfBoundsException]
* A Reference Guide - https://www.baeldung.com/java-arrays-guide[Arrays in Java]


Expand Down
2 changes: 1 addition & 1 deletion rules/S6466/python/metadata.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
77 changes: 41 additions & 36 deletions rules/S6466/python/rule.adoc
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
This rule raises an issue when trying to access a list or a tuple at an 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, potentially
leading to security vulnerabilities.
To fix `IndexError`s, you should always ensure you are accessing sequences
within their bounds.

== Why is this an issue?

In python, lists and tuples have a certain size and their elements are indexed
in the range between the starting index `0` (inclusive) and the length of the
sequence (exclusive).
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.

Expand All @@ -15,23 +22,19 @@ 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, the result must be non-negative and fit into the aforementioned range.

=== What is the potential impact?

Since accessing a sequence outside of its bounds raises an `IndexError`, it will
interrupt the normal execution of the program and can result in unexpected
crashes.
Therefore, this issue might impact the availability and reliability of your
application.

If the computation of the index is tied to user input data, this issue can
potentially be exploited by attackers to disrupt your application.
include::../impact.adoc[]

== How can I fix it?

The following are examples of code containing out-of-bounds accesses to
sequences, resulting in `IndexError`s.
These situations can be avoided by carefully considering the range of valid
index values, or even better, by comparing indices and the size of a sequence.
index values, or even better, by comparing indices and the length of a sequence.

In this first example a list `ls` containing three elements is being created.
Since in Python, 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

Expand All @@ -53,9 +56,13 @@ def fun():
----

The following example is similar, but we don't know the length of the list `ls`
so it is computed using `len(...)`.
Still, accessing a list with its length as an index is not correct:

=== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
[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.
Expand All @@ -64,7 +71,7 @@ def fun(ls: list[int]):

=== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
[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.
Expand All @@ -76,20 +83,17 @@ def fun(ls: list[int]):

=== How does it work?

In the first example a list `ls` containing three elements is being created.
Since in Python, 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`.
You should always ensure that you are accessing sequences using indices that are
within the bounds of the array.
Here are some best practices to follow:

The second example is similar, but we don't know the length of the list `ls` so
it is computed using `len`.
Still, accessing a list with its size as an index is not correct.
Always compare indices and the length of a sequence before accessing elements
using `if-else` constructs or other control flow tools.
The length can be retrieved using the built-in `len(...)` function.
For example, `len(ls)` returns the length of the list `ls`.

In general, when you do not know the concrete size of a sequence that you are
accessing by index, always make sure to guard the access.
That is, you should add if-else-constructs or make use of other control flow
tools to ensure that the index value you are using fits within the bounds of
the sequence.
Use loops to iterate over arrays instead of accessing elements directly.
For example, `for elem in ls` iterates over the list `ls`.

=== Pitfalls

Expand All @@ -100,9 +104,8 @@ indices.

=== Going the extra mile

In many cases, accessing a sequence by index can be avoided.
For instance, you can make use of built-in functions like `map()`, `filter()`
and `reduce()` that let you operate on sequences without using indices.
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
Expand All @@ -111,7 +114,7 @@ retrieve elements from the sequence using indices.

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
[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.
Expand All @@ -121,15 +124,17 @@ for i in range(len(ls)):

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
[source,python,diff-id=3,diff-type=compliant]
----
for i, elem in enumerate(ls):
foo(i, elem)
----

== More Info

=== Documentation
=== Resources

==== Documentation

* https://docs.python.org/3/reference/expressions.html#subscriptions[Subscriptions]
* https://docs.python.org/3/library/exceptions.html#IndexError[`IndexError`]
Expand Down

0 comments on commit 227e4cb

Please sign in to comment.