/*******************************************************************************
* Copyright (c) 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.examples.xss;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.compiler.problem.DefaultProblem;
import org.eclipse.dltk.compiler.problem.IProblem;
import org.eclipse.dltk.compiler.problem.ProblemSeverities;
import org.eclipse.dltk.core.builder.IBuildContext;
import org.eclipse.php.internal.core.compiler.ast.nodes.ArrayVariableReference;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPCallExpression;
import org.eclipse.php.internal.core.compiler.ast.visitor.PHPASTVisitor;
/**
* This validator searches for unsafe places that can be used for XSS, and
* notifies developer about them. Unsafe places are considered to be:
* <ul>
* <li>Using direct reference to URL parameter variables ($_GET, $_POST, $_REQUEST)
* without testing it with isset() and htmlentities() first.</li>
* <li>Add more...</li>
* </ul>
*/
public class XSSValidationVisitor extends PHPASTVisitor {
private IBuildContext context;
private boolean hasSafeCallInParent;
public XSSValidationVisitor(IBuildContext context) {
this.context = context;
}
public boolean visit(PHPCallExpression node) throws Exception {
// Check the parent: it should be either isset() or htmlentities() call
if (node.getReceiver() == null) { // if this is a function call, not method
String funcName = node.getName();
if ("isset".equalsIgnoreCase(funcName) || "htmlentities".equalsIgnoreCase(funcName)) {
hasSafeCallInParent = true;
}
}
return visitGeneral(node);
}
public boolean endvisit(PHPCallExpression node) throws Exception {
hasSafeCallInParent = false;
endvisitGeneral(node);
return true;
}
/**
* Checks whether this variable is a reference to the URL parameter.
* @param s Variable reference node
* @return
*/
protected boolean isURLParemeterVariable(VariableReference s) {
String name = s.getName();
return ("$_GET".equals(name) || "$_POST".equals(name) || "$_REQUEST".equals(name));
}
public boolean visit(ArrayVariableReference s) throws Exception {
if (isURLParemeterVariable(s) && !hasSafeCallInParent) {
context.getProblemReporter().reportProblem(
new DefaultProblem(
context.getFile().getName(),
"Unsafe use of " + s.getName() + ": possible XSS attack",
IProblem.Unclassified,
new String[0],
ProblemSeverities.Warning,
s.sourceStart(),
s.sourceEnd(),
context.getLineTracker().getLineNumberOfOffset(s.sourceStart()))
);
}
return super.visit(s);
}
}