/** Provides classes and predicates to reason about cleartext storage in serializable classes. */ import java import semmle.code.java.frameworks.JAXB import semmle.code.java.dataflow.DataFlow import semmle.code.java.security.CleartextStorageQuery import semmle.code.java.security.CleartextStoragePropertiesQuery private class ClassCleartextStorageSink extends CleartextStorageSink { ClassCleartextStorageSink() { this.asExpr() = getInstanceInput(_, _) } } /** The instantiation of a storable class, which can be stored to disk. */ abstract class ClassStore extends Storable, ClassInstanceExpr { /** Gets an input, for example `input` in `instance.password = input`. */ override Expr getAnInput() { exists(DataFlow::Node instance | ClassStoreFlow::flow(DataFlow::exprNode(this), instance) and result = getInstanceInput(instance, this.getConstructor().getDeclaringType()) ) } } /** * The instantiation of a serializable class, which can be stored to disk. * * Only includes tainted instances where data from a `SensitiveSource` may flow * to an input of the `Serializable`. */ private class Serializable extends ClassStore { Serializable() { this.getConstructor().getDeclaringType().getAnAncestor() instanceof TypeSerializable and // `Properties` are `Serializable`, but handled elsewhere. not this instanceof Properties and // restrict attention to tainted instances exists(SensitiveSource data | data.flowsTo(getInstanceInput(_, this.getConstructor().getDeclaringType())) ) } /** Gets a store, for example `outputStream.writeObject(instance)`. */ override Expr getAStore() { exists(DataFlow::Node n | serializableStore(n, result) and ClassStoreFlow::flow(DataFlow::exprNode(this), n) ) } } /** The instantiation of a marshallable class, which can be stored to disk as XML. */ private class Marshallable extends ClassStore { Marshallable() { this.getConstructor().getDeclaringType() instanceof JaxbElement } /** Gets a store, for example `marshaller.marshal(instance)`. */ override Expr getAStore() { exists(DataFlow::Node n | marshallableStore(n, result) and ClassStoreFlow::flow(DataFlow::exprNode(this), n) ) } } /** Gets an input, for example `input` in `instance.password = input`. */ private Expr getInstanceInput(DataFlow::Node instance, RefType t) { exists(AssignExpr a, FieldAccess fa | instance = DataFlow::getFieldQualifier(fa) and a.getDest() = fa and a.getSource() = result and fa.getField().getDeclaringType() = t | t.getASourceSupertype*() instanceof TypeSerializable or t instanceof JaxbElement ) } private module ClassStoreFlowConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node src) { src.asExpr() instanceof ClassStore } predicate isSink(DataFlow::Node sink) { exists(getInstanceInput(sink, _)) or serializableStore(sink, _) or marshallableStore(sink, _) } int fieldFlowBranchLimit() { result = 1 } } private module ClassStoreFlow = DataFlow::Global; private predicate serializableStore(DataFlow::Node instance, Expr store) { exists(MethodCall m | store = m and m.getMethod() instanceof WriteObjectMethod and instance.asExpr() = m.getArgument(0) ) } private predicate marshallableStore(DataFlow::Node instance, Expr store) { exists(MethodCall m | store = m and m.getMethod() instanceof JaxbMarshalMethod and instance.asExpr() = m.getArgument(0) ) }