-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathNonSerializableField.ql
More file actions
98 lines (84 loc) · 3.33 KB
/
NonSerializableField.ql
File metadata and controls
98 lines (84 loc) · 3.33 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
/**
* @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 + "."