Skip to content

Java: CodeQL and chill missing result #17045

Open
@p1keman

Description

@p1keman

Description of the issue
I'm practicing securitylab's Codeql-and-chill, https://securitylab.github.com/ctf/codeql-and-chill/
and I found four data flows using the following codeql rules, with one missing,However, in my test demo, the missing data stream can be completely followed up. I don't know what causes this. The following is the specific codeql rules and test demo

Code database download link https://github.com/github/securitylab/releases/download/ctf-codeql-and-chill-java-edition/titus-control-plane-db.zip

Here's what my codeql rules say

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import DataFlow::PartialPathGraph

class BuildConstraintViolationWithTemplateMethod extends Method{
    BuildConstraintViolationWithTemplateMethod(){
        hasQualifiedName("javax.validation", ["ConstraintValidatorContext"], "buildConstraintViolationWithTemplate")
    }
}

class BuildConstraintViolationWithTemplateSink extends DataFlow::ExprNode{
    BuildConstraintViolationWithTemplateSink(){
        exists( MethodAccess ma | ma.getCallee() instanceof BuildConstraintViolationWithTemplateMethod | ma.getArgument(0)=asExpr())
    }
}

class CollectionType extends RefType{
    CollectionType(){
        hasQualifiedName("java.util", "Collection<?>")
    }
}

class BeanValidationConfig extends TaintTracking::Configuration {
    BeanValidationConfig() { this = "BeanValidationConfig" }
  
    override predicate isSource(DataFlow::Node source) { 
        source instanceof RemoteFlowSource
    }

    override predicate isSink(DataFlow::Node sink) { 
        sink instanceof BuildConstraintViolationWithTemplateSink
    }
    
    override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
        pred.asExpr() = succ.asExpr().(AddExpr).getAnOperand()  
        or
        exists(MethodAccess ac | pred.asExpr() = ac.getAnArgument() and succ.asExpr() = ac)  
    }

    override predicate isSanitizer(DataFlow::Node n) {
        exists(Method m | m.getParameter(0).getType() instanceof CollectionType|n.asParameter()=m.getParameter(0))
    }

    override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
        super.allowImplicitRead(node, c)
        or
        this.isAdditionalTaintStep(node, _) and
        (
            c instanceof DataFlow::ArrayContent
            or
            c instanceof DataFlow::CollectionContent
            or
            c instanceof DataFlow::MapValueContent
        )
    }
}

from BeanValidationConfig bvc, DataFlow::PathNode source, DataFlow::PathNode sink
where bvc.hasFlowPath(source, sink)
select source.getNode(), "-->", sink

The missing code location is in the /Users/xavier/src/github/titus-control-plane/titus-common/src/main/java/com/netflix/titus/common/model/sanitizer/internal/CollectionValidator.java file

Debugging through hasPartialFlow shows that the last position is in filter(e -> e.getValue() == null)

        if (!constraintAnnotation.allowNullValues()) {
            Set<String> badEntryKeys = value.entrySet().stream()
                    .filter(e -> e.getValue() == null)
                    .map(e -> e.getKey() instanceof String ? (String) e.getKey() : "<not_string>")
                    .collect(Collectors.toSet());
            if (!badEntryKeys.isEmpty()) {
                attachMessage(context, "null values found for keys: " + new TreeSet<>(badEntryKeys));
                return false;
            }
        }

When I tested it with the following code, I found that this rule follows through into the final system.out.println() function
MapToSetTest.java

package com.example.demo;

import java.util.*;
import java.util.stream.Collectors;

public class MapToSetTest {
    public static void main(String[] args) {
        Map<Object, Object> value = new HashMap<>();
        value.put("key1", null);
        value.put(2, null);
        value.put("key3", "value3");
        value.put(4, "value4");

        Set<String> badEntryKeys = value.entrySet().stream()
                .filter(e -> e.getValue() == null)
                .map(e -> e.getKey() instanceof String ? (String) e.getKey() : "<not_string>")
                .collect(Collectors.toSet());
        x("null values found for keys: " +new TreeSet<>( badEntryKeys));
    }

    private static void x(String string){
        System.out.println(string);
    }
}

The test rules are as follows

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import DataFlow::PartialPathGraph

class BeanValidationConfig extends TaintTracking::Configuration {
    BeanValidationConfig() { this = "BeanValidationConfig" }
  
    override predicate isSource(DataFlow::Node source) { 
        exists( Variable v |  v.getName()="value" | v.getInitializer()= source.asExpr())
    }

    override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
        pred.asExpr() = succ.asExpr().(AddExpr).getAnOperand()  
        or
        exists(MethodAccess ac | pred.asExpr() = ac.getAnArgument() and succ.asExpr() = ac)  
    }

    override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
        super.allowImplicitRead(node, c)
        or
        this.isAdditionalTaintStep(node, _) and
        (
            c instanceof DataFlow::ArrayContent
            or
            c instanceof DataFlow::CollectionContent
            or
            c instanceof DataFlow::MapValueContent
        )
    }
    override int explorationLimit() { result =  3}
}

predicate partial_flow(DataFlow::PartialPathNode n, DataFlow::Node src, int dist) {
    exists(BeanValidationConfig conf, DataFlow::PartialPathNode source |
    conf.hasPartialFlow(source, n, dist) and
    src = source.getNode() and source.getNode().getLocation().getFile().getBaseName()="MapToSetTest.java"
    )
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    JavaquestionFurther information is requested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions