/**
* 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 com.h3xstream.findbugs.test.matcher;
import com.h3xstream.findbugs.test.jsp.DebugExtensionExtractor;
import com.h3xstream.findbugs.test.jsp.SmapParser;
import com.h3xstream.findbugs.test.service.ClassFileLocator;
import edu.umd.cs.findbugs.BugInstance;
import org.apache.commons.io.IOUtils;
import org.mockito.Matchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* DSL to build BugInstanceMatcher
*/
public class BugInstanceMatcherBuilder {
private static final Logger log = LoggerFactory.getLogger(BugInstanceMatcherBuilder.class);
private String bugType;
private String className;
private String methodName;
private String fieldName;
private Integer lineNumber;
private Integer lineNumberApprox;
private String priority;
private String jspFile;
private Integer jspLine;
public BugInstanceMatcherBuilder bugType(String bugType) {
this.bugType = bugType;
return this;
}
public BugInstanceMatcherBuilder inClass(String className) {
this.className = className;
return this;
}
public BugInstanceMatcherBuilder inMethod(String methodName) {
this.methodName = methodName;
return this;
}
public BugInstanceMatcherBuilder atField(String fieldName) {
this.fieldName = fieldName;
return this;
}
public BugInstanceMatcherBuilder atLine(int lineNumber) {
this.lineNumber = lineNumber;
return this;
}
/**
* @deprecated Use atJspLine for JSP line mapping
* @param lineNumberApprox Line to verify accepting an offset of 1
* @return
*/
@Deprecated
public BugInstanceMatcherBuilder atLineApprox(int lineNumberApprox) {
this.lineNumberApprox = lineNumberApprox;
return this;
}
/**
* Define the priority of the detector
* @param priority Priority can be "High", "Medium" or "Low"
* @return
*/
public BugInstanceMatcherBuilder withPriority(String priority) {
this.priority = priority;
return this;
}
public BugInstanceMatcherBuilder inJspFile(String jspFile) {
this.jspFile = jspFile;
return this;
}
public BugInstanceMatcherBuilder atJspLine(Integer jspLine) {
this.jspLine = jspLine;
return this;
}
/**
* @return Mockito Matcher
*/
public BugInstance build() {
//JSP line to Java source conversion
List<Integer> multipleChoicesLine = null;
if(jspLine != null) {
if(jspFile != null) {
//Map JSP lines to Java base on the smap file if available
multipleChoicesLine = mapJspToJavaLine(jspFile,jspLine);
}
else {
throw new RuntimeException("JSP file not set.");
}
}
return Matchers.argThat(new BugInstanceMatcher(bugType, className, methodName, fieldName, lineNumber, lineNumberApprox, priority, jspFile, multipleChoicesLine));
}
private static List<Integer> mapJspToJavaLine(String jspFile, Integer jspLine) {
List<Integer> outJavaLines = new ArrayList<>();
ClassFileLocator locator = new ClassFileLocator();
String jspClassLocation = locator.getJspFilePath(jspFile);
File smapFile = new File(jspClassLocation + ".smap");
try {
String debugInfo;
if(smapFile.exists()) {
debugInfo = IOUtils.toString(new FileInputStream(smapFile),"UTF-8");
}
else {
//
if(!new File(jspClassLocation).exists())
throw new RuntimeException("Unable to locate the class file "+ jspClassLocation);
debugInfo = new DebugExtensionExtractor().getDebugExtFromClass(new FileInputStream(jspClassLocation));
if(debugInfo == null)
throw new RuntimeException("SMAP info is missing. ("+smapFile+" or embedded in "+jspClassLocation+")");
}
//Convert
SmapParser smapDebug = new SmapParser(debugInfo);
for(Integer val : smapDebug.getOriginalLine(jspLine)) outJavaLines.add(val);
log.info("The JSP line "+jspLine+" was mapped to "+ Arrays.toString(outJavaLines.toArray()));
if(outJavaLines.isEmpty()) {
throw new RuntimeException("Unable to find the mapping for the JSP line "+jspLine);
}
}
catch (IOException e) {
throw new RuntimeException("Unable to open the smap file.",e);
}
return outJavaLines;
}
}