-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathNonSerializableField.ql
More file actions
99 lines (90 loc) · 3.38 KB
/
NonSerializableField.ql
File metadata and controls
99 lines (90 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* @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.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.getAnAncestor() | serializableOrExternalizable(sup))
or
// Collection interfaces are not serializable, but their implementations are
// likely to be.
collectionOrMapType(t) and
not t instanceof RawType 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.getAStrictAncestor())
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.getFile().isJavaSourceFile() and
c.getAStrictAncestor() 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 + "."