PMD language module that provides static code analysis capabilities for Salesforce Visualforce pages and components
—
Infrastructure for creating custom PMD rules to analyze Visualforce code quality and security issues. This capability provides the foundation for building static analysis rules specific to Visualforce development patterns and security concerns.
Base class for all Visualforce PMD rules, providing visitor pattern integration and rule execution framework.
/**
* Abstract base class for Visualforce PMD rules
* Extends PMD's AbstractRule and implements VfVisitor for AST traversal
* All custom Visualforce rules should extend this class
*/
public abstract class AbstractVfRule extends AbstractRule implements VfVisitor<Object, Object> {
/**
* Apply this rule to an AST node and its descendants
* Entry point called by PMD framework for rule execution
* @param target Root AST node to analyze (typically ASTCompilationUnit)
* @param ctx Rule context containing reporting and configuration
*/
@Override
public void apply(Node target, RuleContext ctx);
/**
* Default visitor implementation that traverses all child nodes
* Override specific visit methods to implement rule logic
* @param node AST node being visited
* @param param Context parameter (typically RuleContext)
* @return Parameter passed through (typically unchanged RuleContext)
*/
@Override
public Object visitNode(Node node, Object param);
}Usage Example:
import net.sourceforge.pmd.lang.visualforce.rule.AbstractVfRule;
import net.sourceforge.pmd.lang.visualforce.ast.*;
/**
* Example rule that checks for hardcoded strings in Visualforce pages
*/
public class NoHardcodedStringsRule extends AbstractVfRule {
@Override
public Object visit(ASTLiteral literal, Object data) {
String value = literal.getImage();
// Check for hardcoded user-facing strings
if (value != null && value.length() > 1 &&
value.startsWith("'") && value.endsWith("'")) {
String content = value.substring(1, value.length() - 1);
if (isUserFacingString(content)) {
addViolation(data, literal,
"Hardcoded user-facing string should use labels or custom settings");
}
}
return super.visit(literal, data);
}
private boolean isUserFacingString(String content) {
// Implementation logic to detect user-facing strings
return content.length() > 10 && !content.matches("^[A-Z_]+$");
}
}Core visitor interface for traversing Visualforce AST nodes with type-safe visit methods.
/**
* Visitor interface for Visualforce AST traversal
* Provides visit methods for all AST node types
* @param <P> Parameter type passed through visitation
* @param <R> Return type from visit methods
*/
public interface VfVisitor<P, R> {
// Root and compilation nodes
R visit(ASTCompilationUnit node, P data);
// Element and structure nodes
R visit(ASTElement node, P data);
R visit(ASTAttribute node, P data);
R visit(ASTAttributeValue node, P data);
R visit(ASTContent node, P data);
// Expression nodes
R visit(ASTElExpression node, P data);
R visit(ASTExpression node, P data);
R visit(ASTDotExpression node, P data);
R visit(ASTNegationExpression node, P data);
R visit(ASTIdentifier node, P data);
R visit(ASTLiteral node, P data);
R visit(ASTArguments node, P data);
// Text and content nodes
R visit(ASTText node, P data);
R visit(ASTCData node, P data);
R visit(ASTHtmlScript node, P data);
// Document structure nodes
R visit(ASTDeclaration node, P data);
R visit(ASTDoctypeDeclaration node, P data);
R visit(ASTDoctypeExternalId node, P data);
// Default visitor behavior for unhandled nodes
R visitNode(Node node, P data);
}Abstract base implementation of VfVisitor providing common traversal patterns.
/**
* Abstract base visitor for Visualforce AST traversal
* Extends PMD's AstVisitorBase with Visualforce-specific functionality
* Provides default implementations that delegate to visitNode()
*/
public abstract class VfVisitorBase<P, R> extends AstVisitorBase<P, R> implements VfVisitor<P, R> {
/**
* Default implementation for all visit methods
* Override specific visit methods to implement custom logic
* @param node AST node being visited
* @param data Context data
* @return Result from processing
*/
@Override
public R visitNode(Node node, P data);
}Usage Example:
import net.sourceforge.pmd.lang.visualforce.ast.VfVisitorBase;
/**
* Visitor to collect all EL expressions in a Visualforce page
*/
public class ElExpressionCollector extends VfVisitorBase<List<ASTElExpression>, List<ASTElExpression>> {
@Override
public List<ASTElExpression> visit(ASTElExpression expression, List<ASTElExpression> expressions) {
// Add this expression to collection
expressions.add(expression);
// Continue traversing children
return super.visit(expression, expressions);
}
public static List<ASTElExpression> collectExpressions(ASTCompilationUnit root) {
List<ASTElExpression> expressions = new ArrayList<>();
ElExpressionCollector collector = new ElExpressionCollector();
collector.visit(root, expressions);
return expressions;
}
}Pre-implemented security rules that demonstrate rule development patterns and provide immediate value for Visualforce security analysis.
/**
* Rule to detect CSRF vulnerabilities in Visualforce pages
* Checks for missing or improperly configured CSRF protection
*/
public class VfCsrfRule extends AbstractVfRule {
/**
* Visit apex:page elements to check CSRF configuration
* @param node ASTElement representing apex:page
* @param data Rule context
* @return Rule context (unchanged)
*/
@Override
public Object visit(ASTElement node, Object data);
}
/**
* Rule to detect XSS vulnerabilities in HTML style tags
* Identifies potentially dangerous style tag content and attributes
*/
public class VfHtmlStyleTagXssRule extends AbstractVfRule {
/**
* Visit HTML style elements and attributes for XSS risks
* @param node ASTElement representing style elements
* @param data Rule context
* @return Rule context (unchanged)
*/
@Override
public Object visit(ASTElement node, Object data);
}
/**
* Rule to detect unescaped Expression Language that could lead to XSS
* Analyzes EL expressions for proper escaping based on context and data types
*/
public class VfUnescapeElRule extends AbstractVfRule {
/**
* Visit EL expressions to check for proper escaping
* @param node ASTElExpression to analyze
* @param data Rule context
* @return Rule context (unchanged)
*/
@Override
public Object visit(ASTElExpression node, Object data);
/**
* Visit elements that contain EL expressions
* @param node ASTElement that may contain expressions
* @param data Rule context
* @return Rule context (unchanged)
*/
@Override
public Object visit(ASTElement node, Object data);
}Usage Example:
import net.sourceforge.pmd.lang.visualforce.rule.security.*;
// Security rules are typically configured in PMD ruleset XML files
// and applied automatically by the PMD engine:
/*
<rule ref="category/visualforce/security.xml/VfCsrfRule" />
<rule ref="category/visualforce/security.xml/VfUnescapeElRule" />
<rule ref="category/visualforce/security.xml/VfHtmlStyleTagXssRule" />
*/
// Rules can also be instantiated and applied programmatically:
public void analyzeSecurityIssues(ASTCompilationUnit ast, RuleContext ctx) {
VfCsrfRule csrfRule = new VfCsrfRule();
VfUnescapeElRule xssRule = new VfUnescapeElRule();
csrfRule.apply(ast, ctx);
xssRule.apply(ast, ctx);
}Standard pattern for developing custom Visualforce analysis rules.
Complete Custom Rule Example:
/**
* Rule to enforce naming conventions for Visualforce components
*/
public class ComponentNamingConventionRule extends AbstractVfRule {
private static final Pattern VALID_COMPONENT_NAME =
Pattern.compile("^[A-Z][a-zA-Z0-9]*$");
@Override
public Object visit(ASTElement element, Object data) {
// Check custom component usage
if (element.isHasNamespacePrefix() &&
"c".equals(element.getNamespacePrefix())) {
String componentName = element.getLocalName();
if (!VALID_COMPONENT_NAME.matcher(componentName).matches()) {
addViolation(data, element,
"Custom component name '" + componentName +
"' should use PascalCase naming convention");
}
}
return super.visit(element, data);
}
@Override
public Object visit(ASTAttribute attribute, Object data) {
// Check attribute naming for custom attributes
String attrName = attribute.getName();
if (attrName.startsWith("data-") &&
!attrName.toLowerCase().equals(attrName)) {
addViolation(data, attribute,
"Data attributes should use lowercase with hyphens");
}
return super.visit(attribute, data);
}
}Visualforce rules integrate seamlessly with PMD's rule execution engine:
The rule framework provides the foundation for comprehensive static analysis of Visualforce applications within Salesforce development workflows.
Install with Tessl CLI
npx tessl i tessl/maven-net-sourceforge-pmd--pmd-visualforce