mirror of
https://github.com/github/codeql.git
synced 2026-02-23 18:33:42 +01:00
105 lines
3.4 KiB
Plaintext
105 lines
3.4 KiB
Plaintext
/**
|
|
* @name Non-serializable field
|
|
* @description A non-transient field in a serializable class must also be serializable
|
|
* otherwise it causes the class to fail to serialize with a 'NotSerializableException'.
|
|
* @kind problem
|
|
* @problem.severity warning
|
|
* @precision low
|
|
* @id java/non-serializable-field
|
|
* @tags reliability
|
|
* maintainability
|
|
* language-features
|
|
*/
|
|
|
|
import java
|
|
import semmle.code.java.JDKAnnotations
|
|
import semmle.code.java.Collections
|
|
import semmle.code.java.Maps
|
|
import semmle.code.java.frameworks.javaee.ejb.EJB
|
|
|
|
predicate externalizable(Interface interface) {
|
|
interface.hasQualifiedName("java.io", "Externalizable")
|
|
}
|
|
|
|
predicate serializableOrExternalizable(Interface interface) {
|
|
externalizable(interface) or
|
|
interface instanceof TypeSerializable
|
|
}
|
|
|
|
predicate collectionOrMapType(RefType t) { t instanceof CollectionType or t instanceof MapType }
|
|
|
|
predicate serializableType(RefType t) {
|
|
exists(RefType sup | sup = t.getASupertype*() | serializableOrExternalizable(sup))
|
|
or
|
|
(
|
|
// Collection interfaces are not serializable, but their implementations are
|
|
// likely to be.
|
|
collectionOrMapType(t) and
|
|
forall(RefType param | param = t.(ParameterizedType).getATypeArgument() |
|
|
serializableType(param)
|
|
)
|
|
)
|
|
or
|
|
exists(BoundedType bt | bt = t | serializableType(bt.getUpperBoundType()))
|
|
}
|
|
|
|
RefType reasonForNonSerializableCollection(ParameterizedType par) {
|
|
collectionOrMapType(par) and
|
|
result = par.getATypeArgument() and
|
|
not serializableType(result)
|
|
}
|
|
|
|
string nonSerialReason(RefType t) {
|
|
not serializableType(t) and
|
|
if exists(reasonForNonSerializableCollection(t))
|
|
then result = reasonForNonSerializableCollection(t).getName() + " is not serializable"
|
|
else result = t.getName() + " is not serializable"
|
|
}
|
|
|
|
predicate exceptions(Class c, Field f) {
|
|
f.getDeclaringType() = c and
|
|
(
|
|
// `Serializable` objects with custom `readObject` or `writeObject` methods
|
|
// may write out the "non-serializable" fields in a different way.
|
|
c.declaresMethod("readObject")
|
|
or
|
|
c.declaresMethod("writeObject")
|
|
or
|
|
// Exclude classes with suppressed warnings.
|
|
c.suppressesWarningsAbout("serial")
|
|
or
|
|
// Exclude anonymous classes whose `ClassInstanceExpr` is assigned to
|
|
// a variable on which serialization warnings are suppressed.
|
|
exists(Variable v |
|
|
v.getAnAssignedValue() = c.(AnonymousClass).getClassInstanceExpr() and
|
|
v.suppressesWarningsAbout("serial")
|
|
)
|
|
or
|
|
f.isTransient()
|
|
or
|
|
f.isStatic()
|
|
or
|
|
// Classes that implement `Externalizable` completely take over control during serialization.
|
|
externalizable(c.getASupertype+())
|
|
or
|
|
// Stateless session beans are not normally serialized during their usual life-cycle
|
|
// but are forced by their expected supertype to be serializable.
|
|
// Arguably, warnings for their non-serializable fields can therefore be suppressed in practice.
|
|
c instanceof StatelessSessionEJB
|
|
or
|
|
// Enum types are serialized by name, so it doesn't matter if they have non-serializable fields.
|
|
c instanceof EnumType
|
|
)
|
|
}
|
|
|
|
from Class c, Field f, string reason
|
|
where
|
|
c.fromSource() and
|
|
c.getASupertype+() instanceof TypeSerializable and
|
|
f.getDeclaringType() = c and
|
|
not exceptions(c, f) and
|
|
reason = nonSerialReason(f.getType())
|
|
select f,
|
|
"This field is in a serializable class, " + " but is not serializable itself because " + reason +
|
|
"."
|