mirror of
https://github.com/github/codeql.git
synced 2026-04-24 08:15:14 +02:00
Java: Add 'Useless serialization member in record class' query
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
## Overview
|
||||
|
||||
Record types were introduced in Java 16 as a mechanism to provide simpler data handling that is an alternative to regular classes. Record classes behave slightly differently during serialization however, namely any `writeObject`, `readObject`, `readObjectNoData`, `writeExternal`, and `readExternal` methods and `serialPersistentFields` fields declared in these classes cannot be used to affect the serialization process of any `Record` data type.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Some level of serialization customization is offered by the Java 16 Record feature; the `writeReplace` and `readResolve` methods in a record that implements `java.io.Serializable` can be used to replace the object to be serialized. Otherwise no further customization of serialization of records is possible, and it is better to consider using a regular class implementing `java.io.Serializable` or `java.io.Externalizable` when customization is needed.
|
||||
|
||||
## Example
|
||||
|
||||
```java
|
||||
record T1() implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {} // NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void readObject(ObjectOutputStream out) throws IOException {}// NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void readObjectNoData(ObjectOutputStream out) throws IOException { // NON_COMPLIANT
|
||||
}
|
||||
}
|
||||
|
||||
record T2() implements Externalizable {
|
||||
|
||||
@Override
|
||||
public void writeExternal(ObjectOutput out) throws IOException { // NON_COMPLIANT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // NON_COMPLIANT
|
||||
}
|
||||
}
|
||||
|
||||
record T3() implements Serializable {
|
||||
|
||||
public Object writeReplace(ObjectOutput out) throws ObjectStreamException { // COMPLIANT
|
||||
return new Object();
|
||||
}
|
||||
|
||||
public Object readResolve(ObjectInput in) throws ObjectStreamException { // COMPLIANT
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- Oracle Serialization Documentation: [Serialization of Records](https://docs.oracle.com/en/java/javase/16/docs/specs/serialization/serial-arch.html#serialization-of-records)
|
||||
- Java Record: [Feature Specification](https://openjdk.org/jeps/395)
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @id java/useless-members-of-the-records-class
|
||||
* @name Useless serialization members of `Records`
|
||||
* @description Using certain members of the `Records` class during serialization will result in
|
||||
* those members being ignored.
|
||||
* @kind problem
|
||||
* @precision very-high
|
||||
* @problem.severity warning
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
from Record record, Member m
|
||||
where
|
||||
record.getAMember() = m and
|
||||
m.hasName([
|
||||
"writeObject", "readObject", "readObjectNoData", "writeExternal", "readExternal",
|
||||
"serialPersistentFields"
|
||||
])
|
||||
select record, "Declaration of useless member $@ found.", m, m.getName()
|
||||
@@ -0,0 +1,42 @@
|
||||
import java.io.*;
|
||||
|
||||
public class Test {
|
||||
record T1() implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0]; // NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {} // NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void readObject(ObjectOutputStream out) throws IOException {}// NON_COMPLIANT
|
||||
|
||||
@Serial
|
||||
private void readObjectNoData(ObjectOutputStream out) throws IOException { // NON_COMPLIANT
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record T2() implements Externalizable {
|
||||
|
||||
@Override
|
||||
public void writeExternal(ObjectOutput out) throws IOException { // NON_COMPLIANT
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // NON_COMPLIANT
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record T3() implements Serializable {
|
||||
|
||||
public Object writeReplace(ObjectOutput out) throws ObjectStreamException { // COMPLIANT
|
||||
return new Object();
|
||||
}
|
||||
|
||||
public Object readResolve(ObjectInput in) throws ObjectStreamException { // COMPLIANT
|
||||
return new Object();
|
||||
}
|
||||
}}
|
||||
@@ -0,0 +1,6 @@
|
||||
| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:7:46:7:67 | serialPersistentFields | serialPersistentFields |
|
||||
| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:10:18:10:28 | writeObject | writeObject |
|
||||
| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:13:18:13:27 | readObject | readObject |
|
||||
| Test.java:4:12:4:13 | T1 | Declaration of useless member $@ found. | Test.java:16:18:16:33 | readObjectNoData | readObjectNoData |
|
||||
| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:24:17:24:29 | writeExternal | writeExternal |
|
||||
| Test.java:21:12:21:13 | T2 | Declaration of useless member $@ found. | Test.java:28:17:28:28 | readExternal | readExternal |
|
||||
@@ -0,0 +1 @@
|
||||
Violations of Best Practice/Records/UselessMembersOfTheRecordsClass.ql
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -source 16 -target 16
|
||||
Reference in New Issue
Block a user