Shallow clones copy the top-level object but keep references to nested objects. Mutating a nested property of a shallow clone can therefore mutate the original object as well.
_.clone() creates a new top-level object, but it does not recursively clone nested objects. As a result, the original value and the
clone can still refer to the same nested object.
When code later mutates a nested property of the clone, the mutation is also visible through the original value. This can cause state to change in a part of the application that the code does not appear to touch.
This rule reports direct assignments, updates, and deletions of statically known nested properties when the value was created by the
clone function from Lodash, Lodash-es, or Underscore.js. The rule is intentionally limited to uniquely bound local clone results. It does
not report aliases, callback arguments, loop variables, dynamic property paths, mutating method calls, or top-level-only mutations.
Unexpected changes to the original value can corrupt application state and make the source of a change difficult to identify. The problem is especially difficult to diagnose when the original value is shared across components, requests, or other parts of the application.
_.clone() is appropriate when the nested state is intentionally shared or when only the top-level properties need to be copied. In
these cases, the issue does not indicate a problem with the code.
If a deep clone is intended, use structuredClone() when the value is structured-cloneable and the runtime supports the API.
structuredClone() is a built-in deep-cloning API, but it cannot clone every JavaScript value and may not preserve all library-specific
behavior. For Lodash values, use _.cloneDeep() instead when the project relies on Lodash semantics or needs to handle values that are not
suitable for structuredClone().
import _ from "lodash"; const copy = _.clone(user); copy.address.city = "Geneva"; // Noncompliant: changes "user.address.city" through a shared nested reference
const copy = structuredClone(user); copy.address.city = "Geneva";
import _ from "underscore"; const copy = _.clone(user); copy.address.city = "Geneva"; // Noncompliant: changes "user.address.city" through a shared nested reference
const copy = structuredClone(user); copy.address.city = "Geneva";
import _ from "lodash"; const copy = _.clone(user); copy.address.city = "Geneva"; // Noncompliant: changes "user.address.city" through a shared nested reference
If the shallow clone is intentional, add an inline // NOSONAR: shared nested state is intentional comment to the mutation line to make
that intent explicit. This is the preferred option because it documents the intent in the source code. Alternatively, accept the issue in SonarQube.
import _ from "lodash"; const copy = _.clone(user); copy.address.city = "Geneva"; // NOSONAR: shared nested state is intentional
_.clone_.cloneDeep_.clonestructuredClone()