All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher Maven / Gradle / Ivy

The newest version!
/**
 * Find Security Bugs
 * Copyright (c) Philippe Arteau, All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package edu.umd.cs.findbugs.test.matcher;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

import edu.umd.cs.findbugs.BugAnnotation;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.ClassAnnotation;
import edu.umd.cs.findbugs.FieldAnnotation;
import edu.umd.cs.findbugs.LocalVariableAnnotation;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.annotations.Confidence;

public class BugInstanceMatcher extends BaseMatcher {

    private static final Pattern ANON_FUNCTION_SCALA_PATTERN = Pattern.compile("\\$\\$anonfun\\$([^\\$]+)\\$");

    private final String bugType;
    private final String className;
    private final String methodName;
    private final String fieldName;
    private final String variableName;
    private final Integer lineNumber;
    private final Integer lineNumberApprox;
    private final Confidence confidence;
    private final String jspFile;
    private final List multipleChoicesLine;

    /**
     * All the parameters are optional. Only the non-null parameters are used.
     *
     * @param bugType
     *            Expected bug type
     * @param className
     *            Class name, matches to any of the following: fully dotted class name, simple name, in case of inner classes the simple name with the container class separated by $.
     * @param methodName
     *            Method name
     * @param fieldName
     *            Field name
     * @param variableName
     *            Variable name
     * @param lineNumber
     *            Line number
     * @param lineNumberApprox
     *            Approximate line for test samples that are unstable (Historically the JSP samples)
     * @param confidence
     *            Confidence
     * @param jspFile
     *            JSP file name
     * @param multipleChoicesLine
     *            At least of the line (JSP samples specific)
     */
    public BugInstanceMatcher(String bugType, String className, String methodName, String fieldName,
            String variableName, Integer lineNumber, Integer lineNumberApprox, Confidence confidence, String jspFile,
            List multipleChoicesLine) {
        this.bugType = bugType;
        this.className = className;
        this.methodName = methodName;
        this.fieldName = fieldName;
        this.variableName = variableName;
        this.lineNumber = lineNumber;
        this.lineNumberApprox = lineNumberApprox;
        this.confidence = confidence;
        this.jspFile = jspFile;
        this.multipleChoicesLine = multipleChoicesLine;
    }

    @SuppressWarnings("boxing")
    @Override
    public boolean matches(Object obj) {
        if (obj instanceof BugInstance) {
            BugInstance bugInstance = (BugInstance) obj;

            boolean criteriaMatches = true;
            if (bugType != null) {
                criteriaMatches &= bugInstance.getType().equals(bugType);
            }
            if (confidence != null) {
                criteriaMatches &= bugInstance.getPriority() == confidence.getConfidenceValue();
            }
            if (className != null) {
                ClassAnnotation classAnn = extractBugAnnotation(bugInstance, ClassAnnotation.class);
                if (classAnn == null) {
                    return false;
                }

                String fullName = classAnn.getClassName();
                int startDot = fullName.lastIndexOf(".") + 1;
                int endDollar = fullName.indexOf('$');
                String simpleName = fullName.substring(startDot, endDollar != -1 ? endDollar : fullName.length());
                String simpleNameInner = fullName.substring(startDot);
                criteriaMatches &= fullName.equals(className) || simpleName.equals(className) || simpleNameInner.equals(className);
            }
            if (methodName != null) {
                MethodAnnotation methodAnn = extractBugAnnotation(bugInstance, MethodAnnotation.class);
                ClassAnnotation classAnn = extractBugAnnotation(bugInstance, ClassAnnotation.class);
                String fullClassName = classAnn.getClassName();
                if (methodAnn == null) {
                    return false;
                }

                if (methodAnn.getMethodName().startsWith("apply") && fullClassName != null) {
                    Matcher m = ANON_FUNCTION_SCALA_PATTERN.matcher(fullClassName);
                    if (m.find()) { //Scala function enclose in
                        criteriaMatches &= methodAnn.getMethodName().equals(methodName) || methodName.equals(m.group(1));
                    }
                } else { //
                    criteriaMatches &= methodAnn.getMethodName().equals(methodName);
                }
            }
            if (fieldName != null) {
                FieldAnnotation fieldAnn = extractBugAnnotation(bugInstance, FieldAnnotation.class);
                if (fieldAnn == null) {
                    return false;
                }
                criteriaMatches &= fieldAnn.getFieldName().equals(fieldName);
            }
            if (variableName != null) {
                LocalVariableAnnotation localVarAnn = extractBugAnnotation(bugInstance, LocalVariableAnnotation.class);
                if (localVarAnn == null) {
                    return false;
                }
                criteriaMatches &= localVarAnn.getName().equals(variableName);
            }
            if (lineNumber != null) {
                SourceLineAnnotation srcAnn = extractBugAnnotation(bugInstance, SourceLineAnnotation.class);
                if (srcAnn == null) {
                    return false;
                }
                criteriaMatches &= srcAnn.getStartLine() <= lineNumber && lineNumber <= srcAnn.getEndLine();
            }
            if (lineNumberApprox != null) {
                SourceLineAnnotation srcAnn = extractBugAnnotation(bugInstance, SourceLineAnnotation.class);
                if (srcAnn == null) {
                    return false;
                }
                criteriaMatches &= srcAnn.getStartLine() - 1 <= lineNumberApprox && lineNumberApprox <= srcAnn.getEndLine() + 1;
            }
            if (jspFile != null) {
                ClassAnnotation classAnn = extractBugAnnotation(bugInstance, ClassAnnotation.class);
                String fullName = classAnn.getClassName().replace(".", "/").replace("_005f", "_").replace("_jsp", ".jsp");
                //String simpleName = fullName.substring(fullName.lastIndexOf("/") + 1);
                criteriaMatches &= fullName.endsWith(jspFile);
            }
            if (multipleChoicesLine != null) {
                SourceLineAnnotation srcAnn = extractBugAnnotation(bugInstance, SourceLineAnnotation.class);
                if (srcAnn == null) {
                    return false;
                }
                boolean found = false;
                for (Integer potentialMatch : multipleChoicesLine) {
                    if (srcAnn.getStartLine() - 1 <= potentialMatch && potentialMatch <= srcAnn.getEndLine() + 1) {
                        found = true;
                        break;
                    }
                }
                //if(!found) {
                //log.info("The bug was between lines "+srcAnn.getStartLine()+" and "+srcAnn.getEndLine());
                //}
                criteriaMatches &= found;
            }
            return criteriaMatches;
        }
        return false;
    }

    private static  T extractBugAnnotation(BugInstance bugInstance, Class annotationType) {
        for (BugAnnotation annotation : bugInstance.getAnnotations()) {
            if (annotation.getClass().equals(annotationType)) {
                return annotationType.cast(annotation);
            }
        }
        return null;
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("BugInstance with:\n");
        if (bugType != null) {
            description.appendText("bugType=").appendValue(bugType).appendText(",");
        }
        if (className != null) {
            description.appendText("className=").appendValue(className).appendText(",");
        }
        if (methodName != null) {
            description.appendText("methodName=").appendValue(methodName).appendText(",");
        }
        if (fieldName != null) {
            description.appendText("fieldName=").appendValue(fieldName).appendText(",");
        }
        if (variableName != null) {
            description.appendText("variableName=").appendValue(variableName).appendText(",");
        }
        if (lineNumber != null) {
            description.appendText("lineNumber=").appendValue(lineNumber).appendText(",");
        }
        if (confidence != null) {
            description.appendText("confidence=").appendValue(confidence);
        }
    }

    @Override
    public void describeMismatch(Object item, Description description) {
        description.appendText("was ");

        if (item instanceof BugInstance) {
            BugInstance bugInstance = (BugInstance) item;
            boolean hasCriteria = bugType != null
                    && className != null
                    && methodName != null
                    && fieldName != null
                    && variableName != null
                    && lineNumber != null;

            if (bugType != null || !hasCriteria) {
                description.appendText("bugType=").appendValue(bugInstance.getType()).appendText(",");
            }
            if (className != null || !hasCriteria) {
                ClassAnnotation classAnn = extractBugAnnotation(bugInstance, ClassAnnotation.class);
                description.appendText("className=").appendValue(classAnn).appendText(",");
            }
            if (methodName != null || !hasCriteria) {
                MethodAnnotation methodAnn = extractBugAnnotation(bugInstance, MethodAnnotation.class);
                description.appendText("methodName=").appendValue(methodAnn).appendText(",");
            }
            if (fieldName != null || !hasCriteria) {
                FieldAnnotation fieldAnn = extractBugAnnotation(bugInstance, FieldAnnotation.class);
                description.appendText("fieldName=").appendValue(fieldAnn).appendText(",");
            }
            if (variableName != null || !hasCriteria) {
                LocalVariableAnnotation localVarAnn = extractBugAnnotation(bugInstance, LocalVariableAnnotation.class);
                description.appendText("variableName=").appendValue(localVarAnn).appendText(",");
            }
            if (lineNumber != null || !hasCriteria) {
                SourceLineAnnotation srcAnn = extractBugAnnotation(bugInstance, SourceLineAnnotation.class);
                description.appendText("lineNumber=").appendValue(srcAnn).appendText(",");
            }
            if (confidence != null || !hasCriteria) {
                description.appendText("confidence=").appendValue(bugInstance.getPriority());
            }
        } else {
            description.appendValue(item);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy