Skip to main content

CLS01-G: Limit Accessibility of Fields


Introduction

Controlling who or what can access and change certain parts of a code is crucial because if all variables can be freely changed, it can lead to chaos, especially when many parts of the code try to change them simultaneously. For example, fields that are too open to changes, like public ones or those referencing mutable objects, can get messed up if multiple threads try to use them simultaneously. (These fields may also be corrupted by multiple threads accessing them concurrently [Bloch 2008].)

Not limiting access to these fields opens the door to problems like data corruption or security breaches. Even when we try to limit access, it's not always foolproof. Some clever tricks, like using reflection, can still bypass these restrictions. So, while controlling field accessibility is important, we also need to think about additional ways to protect our code from unwanted changes or attacks.

Non-compliant Code Example (Public Primitive Field)

In this non-compliant code example, the total field tracks the total number of elements added to and removed from a container using the increment and decrement methods, respectively.

// Noncompliant Code Example

public class Widget {
public var total: int
function increment() : void {
if (total < Integer.MAX_VALUE) {
total++
} else {
throw new ArithmeticException("Overflow")
}
}

function decrement() : void {
if (total > 0) {
total--
} else {
throw new ArithmeticException("Overflow")
}
}
}

As a public field, total can be altered by client code independently of the increment and decrement methods.

Compliant Solution (Property)

Accessor methods provide controlled access to fields outside the package in which their class is declared. This compliant solution defines the total property and stores its value in the _total variable using variable alias syntax. The increment and decrement methods modify its value while preserving class invariants.

// Compliant Solution

public class Widget {
private var _total: int as readonly Total
function increment() : void {
if (total < Integer.MAX_VALUE) {
total++
} else {
throw new ArithmeticException("Overflow")
}
}

function decrement() : void {
if (total > 0) {
total--
} else {
throw new ArithmeticException("Overflow")
}
}
}

If you need to perform additional processing before storing or retrieving property values, you can write either or both getter and setter methods for the property.

Non-compliant Code Example (Public Final Reference Field)

Programmers often incorrectly assume that declaring a field or variable final makes the referenced object immutable. Declaring variables with a primitive type final prevents changes to their values after initialization (by standard Gosu processing). However, declaring the field final only makes the reference immutable when the field has a reference type. The final modifier does not affect the referenced object.

This non-compliant code example declares a final reference to a mutable Helium object with public accessibility:

// Noncompliant Code Example

class Element {
private var _atomicNo: int as AtomicNo = 1

construct(atomicno : int) {
_atomicNo = atomicno
}

function decay() : int {
_atomicNo--
return _atomicNo
}
}

public class Widget {
public final var Helium: Element = new Element(2)
}

var w = new Widget()
print(w.Helium.AtomicNo)
w.Helium.decay()
print(w.Helium.AtomicNo)

Code instantiating the Widget class can read the reference to the Helium object and invoke the decay method on the object, decreasing its atomic number by one (producing a Hydrogen atom).

Non-compliant Code Example (Read-only Reference Property)

While the Gosu readonly modifier can declare Helium as non-assignable, internally the _helium reference remains mutable. Adding final to _helium makes the reference immutable. However, when a property has a reference type, declaring the property readonly only makes the reference itself read-only. The readonly modifier does not affect the referenced object.

This non-compliant code example declares a final, read-only mutable property that provides a reference to a Helium object:

// Noncompliant Code Example

public class Widget {
private final var _helium: Element as readonly Helium = new Element(2)
}

Declaring a reference as a readonly property still allows code that instantiates the Widget class to modify the Helium object.

Compliant Solution (Explicit Getter Method)

This compliant solution defines an explicit getter method for the Helium property:

// Compliant Solution

property get Helium() : Element {
return new Element(_helium)
}

This getter method invokes the copy constructor for the Element class to produce a copy of the Helium object:

// Compliant Solution

class Element {
private var _atomicNo: int as AtomicNo = 1

// copy constructor
construct(elem : Element) {
_atomicNo = elem._atomicNo
}

// . . .
}

The following code now calls the decay method on a copy of the Helium object and does not alter the state of the Widget object:

var w = new Widget()
print(w.Helium.AtomicNo)
w.Helium.decay()
print(w.Helium.AtomicNo)

Ensure that you do not return references to private mutable objects from accessor methods (see OBJ05-J. Do not return references to private mutable class members for details).

Non-compliant Code Example (Public Final Array)

A nonzero-length array is always mutable. Declaring a public final array is a potential security risk as a client may modify the array elements.

// Noncompliant Code Example

public class Widget {
private final var _rgb: String[] as readonly RGB = {"red", "green", "blue"}
}

var w = new Widget()
w.RGB[0] = "purple"

print(Arrays.toString(w.RGB))

Declaring the array reference final prevents modification of the reference but does not prevent clients from modifying the array's contents.

Compliant Solution (Index Getter)

This compliant solution declares a private array and provides public methods to get individual items and array size:

// Compliant Solution

class IndexGetter {
private static final var items:String[] = {/* ... */}

static function retrieveItem(index:int) : String {
return item[index]
}
}

Providing direct access to the array objects themselves is safe because String is immutable.

Compliant Solution (Unmodifiable Wrappers)

This compliant solution constructs an immutable public list from the private array. Thus, it is safe to share immutable objects without the risk that the recipient can modify them [Mettler 2010]. This example is safe because String is immutable.

// Compliant Solution

uses java.util.Arrays
uses java.util.Collections

class UnmodifiableWrapper {
private static final var items:String[] = {/* ... */}

static function retrieveItemList() : List<String> {
return Collections.unmodifiableList(Arrays.asList(items))
}
}

A client can modify neither the original array values nor the public list. For more details about unmodifiable wrappers, refer to OBJ56-J. Provide sensitive mutable classes with unmodifiable wrappers.

Risk Assessment

Failing to limit field accessibility can defeat encapsulation, allow attackers to manipulate fields to violate class invariants, or allow these fields to be corrupted due to concurrent accesses from multiple threads.

RuleSeverityLikelihoodRemediation CostPriorityLevel
CLS01-GMediumHighly LikelyMediumL12L1

Automated Detection

Detection of public and protected fields is trivial; heuristic detection of the presence or absence of accessor methods is straightforward. However, simply reporting all detected cases without suppressing those cases covered by the exceptions to this rule would produce excessive false positives. Sound detection and application of the exceptions to this rule are infeasible; however, heuristic techniques may be helpful.

Additional Resources