diff --git a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll index 8080bd28ab6..506a3456b44 100644 --- a/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/src/semmle/code/java/dataflow/ExternalFlow.qll @@ -87,6 +87,7 @@ private module Frameworks { private import semmle.code.java.security.LdapInjection private import semmle.code.java.security.XPath private import semmle.code.java.security.JexlInjection + private import semmle.code.java.frameworks.spring.Spring } private predicate sourceModelCsv(string row) { diff --git a/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll b/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll index 2b09288610e..648b9beb0a0 100644 --- a/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll +++ b/java/ql/src/semmle/code/java/frameworks/spring/Spring.qll @@ -6,6 +6,7 @@ import semmle.code.java.frameworks.spring.SpringAttribute import semmle.code.java.frameworks.spring.SpringAutowire import semmle.code.java.frameworks.spring.SpringBean import semmle.code.java.frameworks.spring.SpringBeanFile +import semmle.code.java.frameworks.spring.SpringBeans import semmle.code.java.frameworks.spring.SpringBeanRefType import semmle.code.java.frameworks.spring.SpringComponentScan import semmle.code.java.frameworks.spring.SpringConstructorArg diff --git a/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll b/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll new file mode 100644 index 00000000000..6c3936ee772 --- /dev/null +++ b/java/ql/src/semmle/code/java/frameworks/spring/SpringBeans.qll @@ -0,0 +1,41 @@ +import java +import semmle.code.java.dataflow.ExternalFlow + +module SpringBeans { + private class FlowSummaries extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "org.springframework.beans;PropertyValue;false;PropertyValue;(String,Object);;Argument[0];MapKey of Argument[-1];value", + "org.springframework.beans;PropertyValue;false;PropertyValue;(String,Object);;Argument[1];MapValue of Argument[-1];value", + "org.springframework.beans;PropertyValue;false;PropertyValue;(PropertyValue);;Argument[0];Argument[-1];value", + "org.springframework.beans;PropertyValue;false;PropertyValue;(PropertyValue,Object);;MapKey of Argument[0];MapKey of Argument[-1];value", + "org.springframework.beans;PropertyValue;false;getName;;;MapKey of Argument[-1];ReturnValue;value", + "org.springframework.beans;PropertyValue;false;getValue;;;MapValue of Argument[-1];ReturnValue;value", + "org.springframework.beans;PropertyValues;true;getPropertyValue;;;MapValue of Argument[-1];ReturnValue;value", + "org.springframework.beans;PropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of Argument[-1];value", + "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;get;;;MapValue of Element of Argument[-1];ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;getPropertyValue;;;Element of Argument[-1];ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;getPropertyValueList;;;Element of Argument[-1];Element of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value", + "org.springframework.beans;MutablePropertyValues;true;setPropertyValueAt;;;Argument[0];Element of Argument[-1];value" + ] + } + } +} diff --git a/java/ql/test/library-tests/frameworks/spring/beans/Test.java b/java/ql/test/library-tests/frameworks/spring/beans/Test.java new file mode 100644 index 00000000000..0075ea88cf9 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/beans/Test.java @@ -0,0 +1,83 @@ +package generatedtest; + +import org.springframework.beans.PropertyValue; + + +public class Test { + Object getMapKey(Object container) { + return null; + } + + Object getMapValue(Object container) { + return null; + } + + Object newWithMapKey(Object element) { + return null; + } + + Object newWithMapValue(Object element) { + return null; + } + + Object source() { + return null; + } + + void sink(Object o) {} + + public void test() { + // @formatter:off + // "org.springframework.beans;PropertyValue;false;;(String,Object);;Argument[0];MapKey of Argument[-1];value", + { + PropertyValue v = new PropertyValue((String) source(), null); + sink(newWithMapKey(v)); // $hasValueFlow + sink(newWithMapValue(v)); // Safe + } + // "org.springframework.beans;PropertyValue;false;;(String,Object);;Argument[1];MapValue of Argument[-1];value", + { + PropertyValue v = new PropertyValue("", source()); + sink(newWithMapKey(v)); // Safe + sink(newWithMapValue(v)); // $hasValueFlow + } + // "org.springframework.beans;PropertyValue;false;;(PropertyValue);;Argument[0];Argument[-1];value", + { + PropertyValue v1 = new PropertyValue((String) source(), null); + PropertyValue v2 = new PropertyValue(v1); + sink(newWithMapKey(v2)); // $hasValueFlow + sink(newWithMapValue(v2)); // Safe + PropertyValue v3 = new PropertyValue("", source()); + PropertyValue v4 = new PropertyValue(v3); + sink(newWithMapKey(v4)); // Safe + sink(newWithMapValue(v4)); // $hasValueFlow + } + // "org.springframework.beans;PropertyValue;false;;(PropertyValue,Object);;MapKey of Argument[0];MapKey of Argument[-1];value", + // "org.springframework.beans;PropertyValue;false;getName;;;MapKey of Argument[-1];ReturnValue;value", + // "org.springframework.beans;PropertyValue;false;getValue;;;MapValue of Argument[-1];ReturnValue;value", + // "org.springframework.beans;PropertyValues;true;getPropertyValue;;;MapValue of Argument[-1];ReturnValue;value", + // "org.springframework.beans;PropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[0];MapKey of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;add;(String,Object);;Argument[1];MapValue of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(PropertyValue);;Argument[0];Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[0];MapKey of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValue;(String,Object);;Argument[1];MapValue of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapKey of Argument[0];MapKey of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(Map);;MapValue of Argument[0];MapValue of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapKey of Element of Argument[0];MapKey of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of Argument[-1];value", + // "org.springframework.beans;MutablePropertyValues;true;addPropertyValues;(PropertyValues);;MapValue of Element of Argument[0];MapValue of Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;get;;;MapValue of Element of Argument[-1];ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;getPropertyValue;;;Element of Argument[-1];ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;getPropertyValueList;;;Element of Argument[-1];Element of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;getPropertyValues;;;Element of Argument[-1];ArrayElement of ReturnValue;value", + // "org.springframework.beans;MutablePropertyValues;true;setPropertyValueAt;;;Argument[0];Element of Argument[-1];value" + // @formatter:on + + } +} diff --git a/java/ql/test/library-tests/frameworks/spring/beans/options b/java/ql/test/library-tests/frameworks/spring/beans/options new file mode 100644 index 00000000000..31b8e3f6935 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/beans/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.2.3 \ No newline at end of file diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.expected b/java/ql/test/library-tests/frameworks/spring/beans/test.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/library-tests/frameworks/spring/beans/test.ql b/java/ql/test/library-tests/frameworks/spring/beans/test.ql new file mode 100644 index 00000000000..610228239e3 --- /dev/null +++ b/java/ql/test/library-tests/frameworks/spring/beans/test.ql @@ -0,0 +1,67 @@ +import java +import semmle.code.java.dataflow.ExternalFlow +import semmle.code.java.dataflow.TaintTracking +import TestUtilities.InlineExpectationsTest +import semmle.code.java.dataflow.internal.FlowSummaryImpl + +class SummaryModelTest extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + //"package;type;overrides;name;signature;ext;inputspec;outputspec;kind", + "generatedtest;Test;false;getMapKey;;;MapKey of Argument[0];ReturnValue;value", + "generatedtest;Test;false;getMapValue;;;MapValue of Argument[0];ReturnValue;value", + "generatedtest;Test;false;newWithElement;;;Argument[0];Element of ReturnValue;value", + "generatedtest;Test;false;newWithMapKey;;;Argument[0];MapKey of ReturnValue;value", + "generatedtest;Test;false;newWithMapValue;;;Argument[0];MapValue of ReturnValue;value" + ] + } +} + +class ValueFlowConf extends DataFlow::Configuration { + ValueFlowConf() { this = "qltest:valueFlowConf" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("source") + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().hasName("sink") + } +} + +class TaintFlowConf extends TaintTracking::Configuration { + TaintFlowConf() { this = "qltest:taintFlowConf" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().hasName("source") + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().hasName("sink") + } +} + +class HasFlowTest extends InlineExpectationsTest { + HasFlowTest() { this = "HasFlowTest" } + + override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasValueFlow" and + exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + or + tag = "hasTaintFlow" and + exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf | + conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink) + | + sink.getLocation() = location and + element = sink.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java new file mode 100644 index 00000000000..fed50425d90 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/MutablePropertyValues.java @@ -0,0 +1,135 @@ +/* + * Copyright 2002-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.springframework.beans; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Spliterator; +import java.util.stream.Stream; +import org.springframework.lang.Nullable; + +public class MutablePropertyValues implements PropertyValues, Serializable { + public MutablePropertyValues() {} + + public MutablePropertyValues(@Nullable PropertyValues original) {} + + public MutablePropertyValues(@Nullable Map original) {} + + public MutablePropertyValues(@Nullable List propertyValueList) {} + + public List getPropertyValueList() { + return null; + } + + public int size() { + return 0; + } + + public MutablePropertyValues addPropertyValues(@Nullable PropertyValues other) { + return null; + } + + public MutablePropertyValues addPropertyValues(@Nullable Map other) { + return null; + } + + public MutablePropertyValues addPropertyValue(PropertyValue pv) { + return null; + } + + public void addPropertyValue(String propertyName, Object propertyValue) {} + + public MutablePropertyValues add(String propertyName, @Nullable Object propertyValue) { + return null; + } + + public void setPropertyValueAt(PropertyValue pv, int i) {} + + public void removePropertyValue(PropertyValue pv) {} + + public void removePropertyValue(String propertyName) {} + + @Override + public Iterator iterator() { + return null; + } + + @Override + public Spliterator spliterator() { + return null; + } + + @Override + public Stream stream() { + return null; + } + + @Override + public PropertyValue[] getPropertyValues() { + return null; + } + + @Override + public PropertyValue getPropertyValue(String propertyName) { + return null; + } + + public Object get(String propertyName) { + return null; + } + + @Override + public PropertyValues changesSince(PropertyValues old) { + return null; + } + + @Override + public boolean contains(String propertyName) { + return false; + } + + @Override + public boolean isEmpty() { + return false; + } + + public void registerProcessedProperty(String propertyName) {} + + public void clearProcessedProperty(String propertyName) {} + + public void setConverted() {} + + public boolean isConverted() { + return false; + } + + @Override + public boolean equals(@Nullable Object other) { + return false; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public String toString() { + return null; + } + +} diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValue.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValue.java new file mode 100644 index 00000000000..2787aa04f17 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValue.java @@ -0,0 +1,70 @@ +/* + * Copyright 2002-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.springframework.beans; + +import java.io.Serializable; +import org.springframework.lang.Nullable; + +public class PropertyValue implements Serializable { + public PropertyValue(String name, @Nullable Object value) {} + + public PropertyValue(PropertyValue original) {} + + public PropertyValue(PropertyValue original, @Nullable Object newValue) {} + + public String getName() { + return null; + } + + public Object getValue() { + return null; + } + + public PropertyValue getOriginalPropertyValue() { + return null; + } + + public void setOptional(boolean optional) {} + + public boolean isOptional() { + return false; + } + + public synchronized boolean isConverted() { + return false; + } + + public synchronized void setConvertedValue(@Nullable Object value) {} + + public synchronized Object getConvertedValue() { + return null; + } + + @Override + public boolean equals(@Nullable Object other) { + return false; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public String toString() { + return null; + } + +} diff --git a/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValues.java b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValues.java new file mode 100644 index 00000000000..9d5b1f51ec3 --- /dev/null +++ b/java/ql/test/stubs/springframework-5.2.3/org/springframework/beans/PropertyValues.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.springframework.beans; + +import java.util.Iterator; +import java.util.Spliterator; +import java.util.stream.Stream; + +public interface PropertyValues extends Iterable { + @Override + default Iterator iterator() { + return null; + } + + @Override + default Spliterator spliterator() { + return null; + } + + default Stream stream() { + return null; + } + + PropertyValue[] getPropertyValues(); + + PropertyValue getPropertyValue(String propertyName); + + PropertyValues changesSince(PropertyValues old); + + boolean contains(String propertyName); + + boolean isEmpty(); + +}