/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2005-2008 University of Maryland
* Copyright (C) 2008 Google
*
* 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 2.1 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; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.detect;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
public class BadSyntaxForRegularExpression
extends OpcodeStackDetector {
BugReporter bugReporter;
public BadSyntaxForRegularExpression(BugReporter bugReporter) {
this.bugReporter = bugReporter;
}
private void singleDotPatternWouldBeSilly(int stackDepth, boolean ignorePasswordMasking) {
if (ignorePasswordMasking && stackDepth != 1)
throw new IllegalArgumentException("Password masking requires stack depth 1, but is " + stackDepth);
if (stack.getStackDepth() < stackDepth) return;
OpcodeStack.Item it = stack.getStackItem(stackDepth);
Object value = it.getConstant();
if (value == null || !(value instanceof String)) return;
String regex = (String) value;
if (!regex.equals(".")) return;
int priority = HIGH_PRIORITY;
if (ignorePasswordMasking) {
priority = NORMAL_PRIORITY;
OpcodeStack.Item top = stack.getStackItem(0);
Object topValue = top.getConstant();
if (topValue instanceof String) {
String replacementString = (String) topValue;
if (replacementString.toLowerCase().equals("x") || replacementString.equals("-") || replacementString.equals("*") || replacementString.equals("\\*"))
return;
if (replacementString.length() == 1 && getMethodName().toLowerCase().indexOf("pass") >= 0)
priority = LOW_PRIORITY;
}
}
bugReporter.reportBug(new BugInstance(this, "RE_POSSIBLE_UNINTENDED_PATTERN", priority)
.addClassAndMethod(this)
.addCalledMethod(this)
.addSourceLine(this)
);
}
private void sawRegExPattern(int stackDepth) {
sawRegExPattern(stackDepth, 0);
}
private void sawRegExPattern(int stackDepth, int flags) {
if (stack.getStackDepth() < stackDepth) return;
OpcodeStack.Item it = stack.getStackItem(stackDepth);
if (it.getSpecialKind() == OpcodeStack.Item.FILE_SEPARATOR_STRING && (flags & Pattern.LITERAL) == 0) {
bugReporter.reportBug(new BugInstance(this, "RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION",
HIGH_PRIORITY)
.addClassAndMethod(this)
.addCalledMethod(this)
.addSourceLine(this)
);
return;
}
Object value = it.getConstant();
if (value == null || !(value instanceof String)) return;
String regex = (String) value;
try {
Pattern.compile(regex, flags);
} catch (PatternSyntaxException e) {
bugReporter.reportBug(new BugInstance(this, "RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION",
HIGH_PRIORITY)
.addClassAndMethod(this)
.addCalledMethod(this)
.addString(regex)
.addInt(flags)
.addSourceLine(this)
);
}
}
/** return an int on the stack, or 'defaultValue' if can't determine */
private int getIntValue(int stackDepth, int defaultValue) {
if (stack.getStackDepth() < stackDepth) return defaultValue;
OpcodeStack.Item it = stack.getStackItem(stackDepth);
Object value = it.getConstant();
if (value == null || !(value instanceof Integer)) return defaultValue;
return ((Number)value).intValue();
}
@Override
public void sawOpcode(int seen) {
if (seen == INVOKESTATIC
&& getClassConstantOperand().equals("java/util/regex/Pattern")
&& getNameConstantOperand().equals("compile")
&& getSigConstantOperand().startsWith("(Ljava/lang/String;I)")
)
sawRegExPattern(1, getIntValue(0, 0));
else if (seen == INVOKESTATIC
&& getClassConstantOperand().equals("java/util/regex/Pattern")
&& getNameConstantOperand().equals("compile")
&& getSigConstantOperand().startsWith("(Ljava/lang/String;)")
)
sawRegExPattern(0);
else if (seen == INVOKESTATIC
&& getClassConstantOperand().equals("java/util/regex/Pattern")
&& getNameConstantOperand().equals("matches")
)
sawRegExPattern(1);
else if (seen == INVOKEVIRTUAL
&& getClassConstantOperand().equals("java/lang/String")
&& getNameConstantOperand().equals("replaceAll")
) {
sawRegExPattern(1);
singleDotPatternWouldBeSilly(1, true);
}
else if (seen == INVOKEVIRTUAL
&& getClassConstantOperand().equals("java/lang/String")
&& getNameConstantOperand().equals("replaceFirst")
)
{
sawRegExPattern(1);
singleDotPatternWouldBeSilly(1, false);
}
else if (seen == INVOKEVIRTUAL
&& getClassConstantOperand().equals("java/lang/String")
&& getNameConstantOperand().equals("matches")
)
{
sawRegExPattern(0);
singleDotPatternWouldBeSilly(0, false);
}
else if (seen == INVOKEVIRTUAL
&& getClassConstantOperand().equals("java/lang/String")
&& getNameConstantOperand().equals("split")
)
{
sawRegExPattern(0);
singleDotPatternWouldBeSilly(0, false);
}
}
}