<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Extending_Code_Analysis_to_the_Webroot&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Wed, 19 Jun 2013 02:01:25 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Extending_Code_Analysis_to_the_Webroot&amp;diff=9248&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:54, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:54:01 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Extending_Code_Analysis_to_the_Webroot</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Extending_Code_Analysis_to_the_Webroot&amp;diff=8334&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Network Security Tools/TOC}}&lt;br /&gt;
Few static source code analysis tools target security vulnerabilities in popular web application programming languages such as Java/JSP, VB.NET, C#, VBScript (i.e., Active Server Pages), PHP, and Perl. The same tools are common for more traditional languages such as C and C++. Each tool might differ in analysis-engine complexity and ruleset definitions, but the end goal is always the same: to find software flaws. These flaws can comprise poorly written code that results in low-quality software, or insecurely written code that results in security vulnerabilities. Tools designed to detect these flaws often support a single programming language and rely on a default set of rules. Unfortunately, most of these default rules provide little value, given the heterogeneous and custom nature of most production web applications. Typically, production webroots are littered with code written in a variety of scripting languages and contain code developed on object-oriented platforms such as J2EE and .NET. Few tried and true static analysis tools are available for scanning these languages, and those that do exist have few rules (if any) for identifying common web application vulnerabilities.&lt;br /&gt;
&lt;br /&gt;
Despite these shortcomings, this chapter aims to show how you can leverage existing code analysis tools when performing web application security code reviews. To accomplish this goal, the chapter describes a testing approach driven by the identification of symptom code, investigates the freely available static source code analysis tool PMD (''http://pmd.sourceforge.net/''), and offers suggestions for adapting PMD to perform web application security code reviews.&lt;br /&gt;
&lt;br /&gt;
== Attacking Web Applications at the Source ==&lt;br /&gt;
&lt;br /&gt;
Historically, network- and operating system-level vulnerabilities have been the sweet spot for attackers. These days, though, hardened firewalls, patched systems, and secure server configurations make these vulnerabilities less desirable than web applications. By their nature, web applications are designed to be convenient for the end user, and security is either overlooked or built in as an afterthought. Web developers lack the real-world security experience of battle-tested firewall and network administrators, who have been targeted by attackers for years. With little or no security experience, developers are unaware of the insecure coding practices that result in web application vulnerabilities. The solution is to test for these vulnerabilities before attackers find them.&lt;br /&gt;
&lt;br /&gt;
The following are two of the most common testing approaches:&lt;br /&gt;
&lt;br /&gt;
;Black box&lt;br /&gt;
: Via the user interface or any other external entry point, this approach pursues the attack vector that provides most of the unauthorized access to the application and/or underlying systems.&lt;br /&gt;
;White box&lt;br /&gt;
: Via access to application source code only, this approach identifies insecure coding practices.&lt;br /&gt;
&lt;br /&gt;
The ideal strategy combines identifying insecure code at the source and verifying whether the identified code is exploitable through the user interface. This approach illustrates both the impact and cause of web application vulnerabilities. Access to the application source allows the tester to view the application's &amp;quot;true&amp;quot; attack surface.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
In general, an application's''attack surface'' is any interface exposed by default. Attack surface in the context of a web application is any accessible page or file in the webroot, including all parameters accepted by that page or file.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When testing from the source code perspective, it's possible to identify every file, page, and page parameter available to attack. If you're testing solely through the user interface, you might miss a page parameter that is not part of the normal user experience and that provides privileged application access to those who know it exists. With access to the application source, you can more easily identify such back doors and remove them from the code base. In addition, the source answers questions about functionality not readily available through the user interface. For example, when fuzzing page parameters through the user interface, the application might respond with unanticipated behavior.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
In the context of a web application, ''fuzzing'' entails systematically sending HTTP requests with header, cookie, and parameter values containing unexpected strings of varying character combinations, types, and lengths. Of interest are the HTTP response codes and the page content returned by the web application.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The tester can choose to spend time investigating application responses through the user interface, or dive straight into the source to reveal the actual code implementation. Both techniques might eventually yield the same result—a verified vulnerability. The latter of the two techniques has the added advantage of quickly finding not only the vulnerability but also its root cause.&lt;br /&gt;
&lt;br /&gt;
On the other hand, access to a live instance of the application provides a means for verifying whether a piece of code is vulnerable, and more important, whether it is actually exploitable. This level of access provides other testing benefits as well. If you have access to only the application source, it can be difficult to know where to start looking for vulnerabilities. With access to the live application, you can build an initial map of the user experience. Typically, you do this by crawling through the web application using local proxy tools to log every request and response. With a map of the user experience defined by the request and response log, the tester can return to the source and more intelligently target specific areas of code. For example, the proxy logs contain URLs that often map to specific files and classes, providing a starting point for targeting the most relevant code.&lt;br /&gt;
&lt;br /&gt;
When testing with access to the application source, a disciplined approach is required. Large webroots can swamp even the most experienced testers. An initial test plan that first targets high-risk code helps to avoid incomplete results and missed vulnerabilities. The next section of this chapter outlines a repeatable and measurable approach to source code analysis that strives to accomplish the following goals, in the order shown:&lt;br /&gt;
&lt;br /&gt;
# Identify as many potential vulnerabilities/exposures as possible in the allotted time period.&lt;br /&gt;
# Target high-risk vulnerabilities first.&lt;br /&gt;
# Confirm the vulnerability through exploitation.&lt;br /&gt;
&lt;br /&gt;
Before delving into details of how to satisfy these objectives, it's important to understand the architecture and code commonly seen when testing web applications.&lt;br /&gt;
&lt;br /&gt;
=== Scope of a Web Application ===&lt;br /&gt;
&lt;br /&gt;
Depending on its architecture and size, a production web application can reside on a single server or span across many different servers and tiers, as shown in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-FIG-1|Figure 6-1]]. Ideally, a production web application's source is grouped logically into presentation, business, and data layers and is separated physically across tiers. Anyone with experience testing web application security knows this is rarely the case. [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-TABLE-1|Table 6-1]] provides a brief description of the types of code commonly found at each tier.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-TABLE-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 6-1. Typical web application architecture'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Tier !! Code description !! Example code&lt;br /&gt;
|-&lt;br /&gt;
| Client || Client-side/mobile code. || JavaScript, VBScript, ActiveX, Java applets&lt;br /&gt;
|-&lt;br /&gt;
| Frontend || Hosts the user interface (UI)/presentation code. Can also contain business logic and data access code. || ASP (VBScript), ASPX (C#/VB.NET), Java/JSP, PHP, Perl&lt;br /&gt;
|-&lt;br /&gt;
| Middle tier || Hosts code implementing a company's business logic and data access code. || C, C++, C#, VB.NET, Java&lt;br /&gt;
|-&lt;br /&gt;
| Backend || Hosts code for the retrieval and storage of application data. Code can also implement business logic rules. || T-SQL, PL/SQL, MySQL dialect&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-1. Typical web application architecture'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Network Security Tools_I_6_tt261.png|Typical web application architecture]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Symptomatic Code Approach ===&lt;br /&gt;
&lt;br /&gt;
Given the complexity, size, and custom nature of production web applications, the previously outlined testing objectives might seem daunting. However, a defined test plan driven by the identification of symptom code provides the tester with a solid foundation for identifying initial high-risk code. As the tester becomes more familiar with the source, he can change the initial test plans to target discovered instances of insecure code. From the tester's perspective, knowing the types of code that result in one or more security vulnerabilities is the key to finding the causes of those vulnerabilities. The symptomatic code approach relies on the tester understanding not just the common web application vulnerabilities, but more importantly, the insecure coding practices that cause them.&lt;br /&gt;
&lt;br /&gt;
=== Symptom Code ===&lt;br /&gt;
&lt;br /&gt;
As the name of the approach implies, insecure coding practices or techniques that result in web application vulnerabilities are called ''symptoms'' or, more specifically,''symptom code'' . To avoid confusion, the terms ''symptom code''and ''vulnerability'' are defined as follows:&lt;br /&gt;
&lt;br /&gt;
;Symptom code&lt;br /&gt;
: Insecure code or coding practices which often lead to exposures or vulnerabilities in web applications. A symptom is not necessarily exploitable. A particular symptom can lead to single or multiple vulnerabilities.&lt;br /&gt;
;Vulnerability&lt;br /&gt;
: An exploitable symptom that allows an attacker to manipulate the application in a fashion that was not intended by the developer.&lt;br /&gt;
&lt;br /&gt;
Table 6-2 provides a list of example symptoms and the potential vulnerabilities/attacks that stem from them. This list assumes the reader is already familiar with common web application vulnerabilities and attacks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-TABLE-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 6-2. Symptoms of common web application vulnerabilities/attacks'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Symptom !! Vulnerability/attack&lt;br /&gt;
|-&lt;br /&gt;
| Dynamic SQL || SQL injection&lt;br /&gt;
|-&lt;br /&gt;
| Dangerous functions || Buffer overflows&lt;br /&gt;
|-&lt;br /&gt;
| Methods for executing commands || Command injection&lt;br /&gt;
|-&lt;br /&gt;
| File I/O methods || Arbitrary filesystem interaction (i.e., creation/deletion/modification/reading of any file)&lt;br /&gt;
|-&lt;br /&gt;
| Writing inline request objects || Cross-site scripting&lt;br /&gt;
|-&lt;br /&gt;
| Cookie access methods || Broken access control&lt;br /&gt;
|-&lt;br /&gt;
| Hardcoded plain-text passwords || Unauthorized access, information leakage&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The presence of a symptom doesn't guarantee the code has a particular vulnerability. Once you identify a symptom, you need to analyze the surrounding code to determine whether it is used in an insecure manner. For example, the presence of file I/O methods in the application source doesn't necessarily mean arbitrary filesystem interaction is possible. However, if the code uses a path location from an external input source to access the local filesystem, it will likely result in an arbitrary filesystem interaction vulnerability. With access to a live instance of the application, you can further verify the exploitability of this vulnerability.&lt;br /&gt;
&lt;br /&gt;
The strength of an experienced tester is knowledge of symptom code and poor coding techniques that lead to application vulnerabilities. A skilled tester works with a defined set of insecure code instances, techniques, and conditions (similar to those shown in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-TABLE-1|Table 6-1]]), which should be flagged at the beginning of a review. This list provides the tester with an initial test plan for quickly identifying easily exploited vulnerabilities. Then the tester can concentrate on less common vulnerabilities specific to the current application.&lt;br /&gt;
&lt;br /&gt;
=== User-Controllable Input ===&lt;br /&gt;
&lt;br /&gt;
Most web application vulnerabilities stem from poorly validated, user-controllable input—''or any data accepted into the application, regardless of method or source''. Typically, the data is sent between client and server in either direction and is completely controllable by the user, regardless of where in the HTTP(S) request it is found (&amp;lt;tt&amp;gt;GET&amp;lt;/tt&amp;gt;/&amp;lt;tt&amp;gt;POST&amp;lt;/tt&amp;gt; parameters, headers, etc.). When testing from the source, we might consider identifying each potential user input and tracing its data path through the code. Once the application accepts the input data it typically reassigns it to variables, carries it across multiple layers of code, and uses it in some transaction or database query. Eventually, the data might return to the user on a similar or alternative data path. The problem is that some paths might lead to symptom code, and others might not. In addition, applications with a large number of inputs increase the likelihood for multiple complex data paths, so tracing data paths from the point of input is inefficient. Given time-constrained testing windows, a more efficient approach is to target symptom code first and trace the paths of any related data out to sources of user-controllable input.&lt;br /&gt;
&lt;br /&gt;
== Toolkit 101 ==&lt;br /&gt;
&lt;br /&gt;
The symptomatic code approach requires a combination of manual and automated testing tools. At a minimum, these tools must include the following:&lt;br /&gt;
&lt;br /&gt;
;Source code viewer&lt;br /&gt;
: The tester uses this tool, which typically is a text editor, to browse through the source or drill down a particular piece of code flagged by static analysis tools. When available, an Integrated Development Environment (IDE) is a powerful tool for quickly navigating through sources and tracing method-call hierarchies.&lt;br /&gt;
;Vulnerability tracking database&lt;br /&gt;
: This isn't a testing tool, but no discussion of source code analysis is complete without mentioning the need to track identified vulnerabilities. Tracking can range from recording issues in a simple text file to logging them in a bug-tracking database such as Bugzilla. At a minimum, the database should provide a place to document the vulnerability, including file location and line number of the insecure code, and steps for reproducing the vulnerability. Documenting this type of information can be a nuisance. You realize its true value only when presenting findings to management or developers.&lt;br /&gt;
;Static analysis tools&lt;br /&gt;
: These tools assist the tester by pointing to specific lines of code, which can be examined more closely within the source code viewer. Database-driven scanning tools that have plug-ins for popular IDEs are ideal. From the IDE console, they allow the tester to launch and view scan results as well as drill down on individual instances of flagged code with a single click.&lt;br /&gt;
&lt;br /&gt;
Static analysis tools are the core component of the tester's toolkit. At a minimum, these tools employ pattern-matching technology common to utilities such as ''grep'' , and most database-driven source code scanning tools such as Flawfinder and RATS. Patterns constructed for these tools can represent a simple string or a complex regular expression. The primary benefit of a utility such as ''grep'' is ad hoc searches of the source, whereas scanning tools provide a default set of rules for identifying insecure code. Some scanning tools have knowledge about the semantics of the target code, allowing for more intelligent analysis than traditional pattern-matching utilities. ''grep'' is valuable when database-driven scanning tools are not available for the target source. This is often the case for web application scripting technologies such as Active Server Pages (VBScript).&lt;br /&gt;
&lt;br /&gt;
The output from static analysis tools produced at the beginning of the review provide an initial road map for identifying known or suspected patterns of insecure code. These tools facilitate tracking down instances of custom code that the tester might otherwise notice only once he's familiar with the source. Compiling a robust symptom code database improves the effectiveness of static analysis tools.&lt;br /&gt;
&lt;br /&gt;
=== Symptom Code Databases ===&lt;br /&gt;
&lt;br /&gt;
A symptom code database serves as an initial test plan at the start of each code review and can be continuously updated as new symptoms are discovered. How you construct symptom code depends on which static analysis tool you use and the programming languages it supports. Pattern-matching tools describe symptom code as a combination of regular expressions, and you can build custom regular expressions for any programming language (VBScript, C#, VB.NET, Java, PHP, etc.). [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-TABLE-3|Table 6-3]] is an updated version of [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-TABLE-2|Table 6-2]] that includes examples of Perl 5 regular expressions representing potential Java symptom code.&lt;br /&gt;
&lt;br /&gt;
This is not a complete list of potential symptom code regular expressions. In fact, some of these examples might produce false positives, and others might produce false negatives. All special characters that are to be treated as literals are escaped with the &amp;lt;tt&amp;gt;\&amp;lt;/tt&amp;gt; character.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-TABLE-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 6-3. Java symptom code'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Symptom !! Perl 5 regexes for Java code !! Vulnerability/attack&lt;br /&gt;
|-&lt;br /&gt;
| Dynamic SQL || &lt;br /&gt;
&lt;br /&gt;
 select.+from&lt;br /&gt;
 insert.+into&lt;br /&gt;
 update.+set&lt;br /&gt;
| SQL injection&lt;br /&gt;
|-&lt;br /&gt;
| Methods for executing commands || &lt;br /&gt;
&lt;br /&gt;
 (Runtime&amp;amp;#124;getRuntime\(\)){0,1}\.exec&lt;br /&gt;
| Command injection&lt;br /&gt;
|-&lt;br /&gt;
| File I/O methods || &lt;br /&gt;
&lt;br /&gt;
 new\s+(java\.io\.){0,1}File\s*\(&lt;br /&gt;
 new\s+(java\.io\.){0,1}FileReader\s*\(&lt;br /&gt;
| Arbitrary file creation, reading&lt;br /&gt;
|-&lt;br /&gt;
| Writing inline request objects || &lt;br /&gt;
&lt;br /&gt;
 \&amp;lt;\s*%\s*=.+request&lt;br /&gt;
| Cross-site scripting&lt;br /&gt;
|-&lt;br /&gt;
| Cookie access methods || &lt;br /&gt;
&lt;br /&gt;
 getCookies&lt;br /&gt;
 addCookie&lt;br /&gt;
| Broken access control&lt;br /&gt;
|-&lt;br /&gt;
| Plaintext database connection strings || &lt;br /&gt;
&lt;br /&gt;
 jdbc\:&lt;br /&gt;
| Information leakage, unauthorized access&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You should also build regular expressions to flag code that might indicate secure coding practices, such as possible sanitization attempts. By quickly identifying possible sanitization techniques, you might save time overall by avoiding blind exploitation attempts and tailoring attacks to subvert known validation logic. An example of this might be the inclusion of a single JSP file that houses methods for certain input validation routines:&lt;br /&gt;
&lt;br /&gt;
 \@\s*include\s+file\s*=\s*\&amp;quot;validate\.jsp\&amp;quot;&lt;br /&gt;
&lt;br /&gt;
As you become more familiar with the code base during the review, you can tune the regular expressions to more accurately capture symptom code. For example, if the code is well documented, it might be useful to search for all instances of a particular developer's name. The analysis tool can run multiple times against the same source tree, revealing new symptom code on each pass. A systematic and iterative approach to source code analysis ensures greater code coverage, increased symptom code detection, and ultimately, real vulnerability identification.&lt;br /&gt;
&lt;br /&gt;
Source code analysis tools and symptom code databases are just components of the symptomatic code approach and they can't find all vulnerabilities. The tool is only as good as its symptom database and the tester's ability to construct meaningful regular expressions. It's important to remember that source code analysis tools and symptom code databases are intended to equip and enable the tester, not to provide a complete solution.&lt;br /&gt;
&lt;br /&gt;
== PMD ==&lt;br /&gt;
&lt;br /&gt;
Anyone who has ever performed a code review of C or C++ code is probably familiar with tools such as Flawfinder and RATS, which rely on pattern matching and have some understanding of the target code. Unfortunately, these tools have vulnerability databases geared primarily toward C and C++ and they are limited in scope.&amp;lt;ref&amp;gt;In addition to C and C++, RATS also scans Perl, PHP, and Python code.&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
PMD is a static source code analysis tool for Java maintained by Tom Copeland at ''http://pmd.sourceforge.net''. It performs a number of checks for poor coding practices, but it doesn't provide any rules for identifying common web application vulnerabilities. A detailed explanation of how PMD works is outside the scope of this chapter. Besides, Tom has already done a good job of it (see ''http://www.onjava.com/pub/a/onjava/2003/02/12/static_analysis.html''). PMD's analysis engine converts each Java source file into a nodelike tree structure called an ''Abstract Syntax Tree'' (AST). Then rules can traverse or &amp;quot;visit&amp;quot; the AST using the &amp;lt;tt&amp;gt;Visitor&amp;lt;/tt&amp;gt; pattern, looking for object patterns that represent problems in the code. The advantage of this technique over pattern-matching tools is that the source is broken into logical chunks or ''tokens'' , allowing for intelligent automated analysis of surrounding code.&lt;br /&gt;
&lt;br /&gt;
=== PMD Rulesets ===&lt;br /&gt;
&lt;br /&gt;
PMD comes prepackaged with a number of rules, but this tool's real strength is the ease with which you can create custom rules. The prepackaged ''rulesets'' deal primarily with software quality issues and include the following categories:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| Basic || Braces&lt;br /&gt;
|-&lt;br /&gt;
| Naming || Code Size&lt;br /&gt;
|-&lt;br /&gt;
| Unused Code || JavaBeans©&lt;br /&gt;
|-&lt;br /&gt;
| Design || Finalizers&lt;br /&gt;
|-&lt;br /&gt;
| Import Statements || Coupling&lt;br /&gt;
|-&lt;br /&gt;
| JUnit Tests || Strict Exceptions&lt;br /&gt;
|-&lt;br /&gt;
| Strings || Controversial&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next section builds an example rule to identify code symptomatic of SQL injection vulnerabilities. Although the focus is PMD, the important point is that any static analysis tool that supports custom rule creation can be extended in a similar way. The tester can leverage the existing analysis engine and rules of a particular tool and simply extend the rule base to incorporate web application code signatures. Ideally, you can add to the rule base (i.e., symptom code database) any code that causes application security issues by describing it in the tool's rule definition syntax.&lt;br /&gt;
&lt;br /&gt;
A PMD ruleset is a XML file that consists of one or more rule elements. Each rule element consists of attributes and child elements, such as the following:&lt;br /&gt;
&lt;br /&gt;
* Name&lt;br /&gt;
* Message&lt;br /&gt;
* Class&lt;br /&gt;
* Description&lt;br /&gt;
* Priority&lt;br /&gt;
* Example&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;Class&amp;lt;/tt&amp;gt; attribute points to the implementation of the rule logic, which can be written as a Java class file or as an &amp;lt;tt&amp;gt;XPath&amp;lt;/tt&amp;gt; expression. A discussion of &amp;lt;tt&amp;gt;xpath&amp;lt;/tt&amp;gt; is outside the scopt of this chapter, but plenty of good &amp;lt;tt&amp;gt;xpath&amp;lt;/tt&amp;gt; resources are available on the internet. The other elements and attributes are informational and can be included in the resulting report. The following example describes a ruleset looking for dynamic SQL:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ruleset name=&amp;quot;Dynamic SQL Ruleset&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;desciption&amp;gt;&lt;br /&gt;
This ruleset contains a collection of rules that find instances of &lt;br /&gt;
potentially exploitable dynamic SQL.&lt;br /&gt;
  &amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;rule name=&amp;quot;DynamicSqlSelectStmts&amp;quot;&lt;br /&gt;
        message=&amp;quot;DYNAMIC SQL ''{0}'' DETECTED&amp;quot;&lt;br /&gt;
        class=&amp;quot;net.sourceforge.pmd.security.web.DynSqlSelectStmts&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;description&amp;gt;&lt;br /&gt;
Dynamic SQL or &amp;quot;string building&amp;quot; techniques that rely on unsanitized input &lt;br /&gt;
are potentially vulnerable to SQL Injection.&lt;br /&gt;
    &amp;lt;/description&amp;gt;&lt;br /&gt;
      &amp;lt;priority&amp;gt;1&amp;lt;/priority&amp;gt;&lt;br /&gt;
    &amp;lt;example&amp;gt;&lt;br /&gt;
&amp;lt;![CDATA[&lt;br /&gt;
...&lt;br /&gt;
int id = request.getParameter(&amp;quot;id&amp;quot;);&lt;br /&gt;
...&lt;br /&gt;
String sql = &amp;quot;select * from employees where employeeid = &amp;quot; + id;&lt;br /&gt;
...&lt;br /&gt;
]]&amp;gt;&lt;br /&gt;
    &amp;lt;/example&amp;gt;&lt;br /&gt;
  &amp;lt;/rule&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- MORE RULES --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ruleset&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We'll visit this example rule in more detail in the [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#Extending PMD|Section 6.4]] later in this chapter.&lt;br /&gt;
&lt;br /&gt;
=== Installing and Running PMD ===&lt;br /&gt;
&lt;br /&gt;
PMD runs on any Windows or *nix system with the following installed:&lt;br /&gt;
&lt;br /&gt;
* JDK 1.3 or higher&lt;br /&gt;
* WinZip or the Unix zip utility from Info-ZIP&lt;br /&gt;
&lt;br /&gt;
You can download PMD as either a binary or a source distribution at ''http://sourceforge.net/project/showfiles.php?group_id=56262''. To install PMD from the command line:&lt;br /&gt;
&lt;br /&gt;
 C:\&amp;gt;'''unzip -q pmd-src-x.y.zi'''&lt;br /&gt;
                   '''p'''&lt;br /&gt;
 C:\&amp;gt;'''cd pmd-x.y'''&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
To test PMD from the command line:&lt;br /&gt;
&lt;br /&gt;
 C:\pmd-x.y&amp;gt;'''cd etc'''&lt;br /&gt;
 C:\pmd-x.y\etc&amp;gt; '''pmd ..\mysourcefile.java html ..\rulesets\basic.xml &amp;gt; out.html'''&lt;br /&gt;
                &lt;br /&gt;
&lt;br /&gt;
You can also install PMD as a plug-in to many popular IDEs. Refer to the PMD SourceForge.net home page for a current list of supported IDEs. It's advantageous to run PMD within an IDE because the tester can immediately jump to vulnerable code, whereas from the command line PMD shows the line number and description of the offending code.&lt;br /&gt;
&lt;br /&gt;
== Extending PMD ==&lt;br /&gt;
&lt;br /&gt;
PMD's default rulesets serve as a solid foundation for developing new rulesets to find common insecure coding practices. Initial rules should target code that's high-risk and easily exploitable. Dynamically building SQL statements with user-controllable input is a good example of high-risk code commonly vulnerable to SQL injection. Rule implementations should be adaptable to new or previously unseen custom code. This is an important feature, as web applications differ in how they implement common functionality, such as authentication, authorization, and data access.&lt;br /&gt;
&lt;br /&gt;
What follows is a walkthrough of a web application security rule that flags symptom code commonly vulnerable to SQL injection—SQL &amp;lt;tt&amp;gt;select&amp;lt;/tt&amp;gt; statements concatenated with user-controllable input. The &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; class implements the rule logic and is located in the ''net.sourceforge.pmd.rules.web.security'' package. This implementation doesn't cover every potential instance of dynamic SQL. It serves only as a guide for writing future security rules that target a variety of symptom code.&lt;br /&gt;
&lt;br /&gt;
=== Objectives ===&lt;br /&gt;
&lt;br /&gt;
The primary objective of &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; is to identify and report dynamically built SQL statements embedded in Java code. For each instance of dynamic SQL, the class identifies and reports any concatenated expressions, such as variables and method calls that return data. Then the rule traces each expression to determine whether they are linked to sources of user-controllable input. Ultimately, the rule generates a list of PMD security violations that serve as a test plan for manually verifying SQL Injection vulnerabilities.&lt;br /&gt;
&lt;br /&gt;
For the purposes of this rule, method arguments/parameters in the source are considered user-controllable input. Therefore, data tracing from identified symptom code to sources of user-controllable input is limited to the scope of a single method.&lt;br /&gt;
&lt;br /&gt;
Consider the simple &amp;lt;tt&amp;gt;doGet&amp;lt;/tt&amp;gt; method in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-1|Example 6-1]]. Based on the outlined objectives, the class should accomplish the following, in the order shown:&lt;br /&gt;
&lt;br /&gt;
# Identify and report the dynamic SQL statement.&lt;br /&gt;
# Identify the concatenated variable &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt;, and trace it back to the &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt; object (i.e., user-controllable input).&lt;br /&gt;
# Halt the trace upon reaching the &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt; object and report the finding.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt; object is a source of user-controllable input and therefore warrants close manual inspection, especially if it's related to a security violation PMD has reported. In the big picture of code review, do not forget to investigate the &amp;lt;tt&amp;gt;response&amp;lt;/tt&amp;gt; object for potential security vulnerabilities.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-1. Simplified doGet method'''&lt;br /&gt;
&lt;br /&gt;
                      '''public void''' doGet(HttpServletRequest request, HttpServletResponse response)&lt;br /&gt;
                                '''throws''' ServletException, IOException {&lt;br /&gt;
  &lt;br /&gt;
     ...  &lt;br /&gt;
     String id = request.getParameter(&amp;quot;id&amp;quot;);&lt;br /&gt;
     ...  &lt;br /&gt;
     String strSql = &amp;quot;SELECT * FROM USERS WHERE ID = '&amp;quot; + id + &amp;quot;'&amp;quot;;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Code Walkthrough ===&lt;br /&gt;
&lt;br /&gt;
The following code walkthrough includes only the code most relevant to the functionality of the &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; class. To view the source code in its entirety, see [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-6|Example 6-6]] later in this chapter.&lt;br /&gt;
&lt;br /&gt;
The ''SqlInjectionExample.java'' example source file (refer to [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-5|Example 6-5]]) is referenced throughout the code walkthrough to help you better understand the class implementation of &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; class imports the ''net.sourceforge.pmd.ast'' package, much like other PMD rules. Where this rule differs is the ''org.apache.regexp'' package, which provides an API for building regular expressions.&lt;br /&gt;
&lt;br /&gt;
                   '''package''' net.sourceforge.pmd.rules.web.security;&lt;br /&gt;
 &lt;br /&gt;
 '''import''' net.sourceforge.pmd.ast.*;&lt;br /&gt;
 &lt;br /&gt;
 '''import''' org.apache.regexp.*;&lt;br /&gt;
 '''import''' java.util.*;&lt;br /&gt;
 '''import''' java.text.MessageFormat;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
You can download the ''regexp'' package from the Apache Jakarta Project web site at ''http://jakarta.apache.org''. You can substitute this package for any other available Java API supporting regular expressions.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Similar to other PMD rules, &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; extends the &amp;lt;tt&amp;gt;AbstractRule&amp;lt;/tt&amp;gt; base class. The &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; class variable controls the printing of debug statements to standard out at runtime:&lt;br /&gt;
&lt;br /&gt;
                   '''public class''' DynSqlSelectStmts '''extends''' AbstractRule {&lt;br /&gt;
     &lt;br /&gt;
     '''private static boolean''' debug = '''true''';&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
If you're working with PMD for the first time, you'll want to leave &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;PATTERN&amp;lt;/tt&amp;gt; variable is initialized with the &amp;lt;tt&amp;gt;select.+from&amp;lt;/tt&amp;gt; string—a regular expression to identify SQL &amp;lt;tt&amp;gt;select&amp;lt;/tt&amp;gt; statements. The syntax of this regular expression matches strings containing the words ''select'' and ''from'', with one or more of any character in between. You can adapt this regular expression to match other types of dynamic SQL, such as &amp;lt;tt&amp;gt;insert into&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;update&amp;lt;/tt&amp;gt; statements.&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                   '''private static final''' String PATTERN = &amp;quot;select.+from&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
At first glance, the &amp;lt;tt&amp;gt;select.+from&amp;lt;/tt&amp;gt; pattern seems like it will generate a large number of false positives. For example, commented code or HTML could contain strings with a similar pattern. Unlike pattern-matching tools, PMD can minimize these false positives. By generating an AST from the source file, our class can drill down on specific code (such as string concatenation), look at surrounding nodes for additional symptoms, and more accurately report whether a pattern is potentially vulnerable to SQL injection.&lt;br /&gt;
&lt;br /&gt;
The next set of variables store data about the current method being visited in the AST. For simplicity these are prefixed with the description &amp;lt;tt&amp;gt;currMeth&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                   '''private '''String currMethName;&lt;br /&gt;
     '''private int''' currMethXsVis;&lt;br /&gt;
     '''private''' Map currMethParams;&lt;br /&gt;
     '''private''' String currMethSymptomCode;&lt;br /&gt;
     '''private''' List currMethExprsToChase;&lt;br /&gt;
     '''private''' List currMethVarsChased;&lt;br /&gt;
&lt;br /&gt;
Here is a brief description of each variable:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;currMethName&amp;lt;/tt&amp;gt;&lt;br /&gt;
: String representing the method name.&lt;br /&gt;
;&amp;lt;tt&amp;gt;currMethXsVis&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Maintains the number of visits to the method.&lt;br /&gt;
;&amp;lt;tt&amp;gt;currMethParams&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Stores the name and type of each method parameter as a set of key/value pairs.&lt;br /&gt;
;&amp;lt;tt&amp;gt;currMethSymptomCode&amp;lt;/tt&amp;gt;&lt;br /&gt;
: String representing the SQL select statement potentially vulnerable to SQL Injection.&lt;br /&gt;
;&amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt;&lt;br /&gt;
: List of expressions (variables, method calls returning data, etc.) concatenated to the identified SQL statement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt;&lt;br /&gt;
&amp;lt;dt&amp;gt;&amp;lt;tt&amp;gt;currMethVarsChased&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;dd&amp;gt;Maintains an ordered list of expressions that are initialized when tracing from symptom code to sources of user-controllable input.&lt;br /&gt;
&lt;br /&gt;
Refer back to the &amp;lt;tt&amp;gt;doGet&amp;lt;/tt&amp;gt; method in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-1|Example 6-1]]. After tracing from the SQL select statement to &amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt; (user-controllable input), &amp;lt;tt&amp;gt;currMethVarsChased&amp;lt;/tt&amp;gt; would contain the following:&lt;br /&gt;
&amp;lt;/dl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Position !! Value&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &lt;br /&gt;
&lt;br /&gt;
 request.getParameter&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &lt;br /&gt;
&lt;br /&gt;
 id&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;DynS&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;qlSelectStmts&amp;lt;/tt&amp;gt; class implements &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; methods for each node of interest when traversing the AST. The remaining code walkthrough focuses on the &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method for each of the following AST nodes:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;ASTCompilationUnit&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;ASTClassBodyDeclaration&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;ASTMethodDeclarator&amp;lt;/tt&amp;gt;&lt;br /&gt;
* &amp;lt;tt&amp;gt;ASTAdditiveExpression&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Relevant sections of the ''SqlInjectionExample.java'' AST are shown with each &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method to help you follow the logic (refer to [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-5|Example 6-5]] for the full source code of this file). In addition, you might want to have the following:&lt;br /&gt;
&lt;br /&gt;
* EBNF grammar reference&amp;lt;ref&amp;gt;You can review the EBNF grammar reference at ''http://cvs.sourceforge.net/viewcvs.py/pmd/pmd/etc/grammar/Java1.4-c.jjt?rev=1.3&amp;amp;content-type=text/vnd.viewcvs-markup''.&amp;lt;/ref&amp;gt; (useful for understanding the structure of the AST)&lt;br /&gt;
* AST Viewer and Designer utilities included with your downloaded PMD distribution (paste the Java source into these utilities to see the full AST)&lt;br /&gt;
* PMD API documentation&lt;br /&gt;
&lt;br /&gt;
==== ASTCompilationUnit ====&lt;br /&gt;
&lt;br /&gt;
Here is the relevant node for &amp;lt;tt&amp;gt;ASTCompilationUnit&amp;lt;/tt&amp;gt; from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
      CompilationUnit&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;ASTCompilationUnit&amp;lt;/tt&amp;gt; node is always the first node and the code contained in its &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method is executed for each source file scanned:&lt;br /&gt;
&lt;br /&gt;
 public Object visit(ASTCompilationUnit node, Object data)&lt;br /&gt;
 {&lt;br /&gt;
          getInfo(node);&lt;br /&gt;
          printDebug(&amp;quot;Rule: &amp;quot; + this.getName( ) + &amp;quot;\n\n&amp;quot;);&lt;br /&gt;
          return super.visit(node,data);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Every &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method in the class begins with a call to &amp;lt;tt&amp;gt;getInfo&amp;lt;/tt&amp;gt;, which retrieves the class name and scope of the node and prints this information to standard out. The &amp;lt;tt&amp;gt;printDebug&amp;lt;/tt&amp;gt; method prints debug statements to standard out (if &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true)&amp;lt;/tt&amp;gt;. To programmatically enable and disable debugging code, the &amp;lt;tt&amp;gt;setDebug&amp;lt;/tt&amp;gt; convenience method is provided. By returning with a call to &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; of the super class, &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; continues analyzing child nodes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
To make the code more readable, subsequent debug statements have been removed.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ASTClassBodyDeclaration ====&lt;br /&gt;
&lt;br /&gt;
Here are the relevant nodes for &amp;lt;tt&amp;gt;ASTClassBodyDeclaration&amp;lt;/tt&amp;gt; from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
      ClassBodyDeclaration&lt;br /&gt;
       MethodDeclaration:(public)&lt;br /&gt;
&lt;br /&gt;
Because the identification of symptom code is on a per-method basis, &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; looks at the immediate child node of &amp;lt;tt&amp;gt;ASTClassBodyDeclaration&amp;lt;/tt&amp;gt; to check whether it is an instance of &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt;. If it is not a method declaration, it returns &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; to avoid unnecessary visits to children nodes.&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''public''' Object visit(ASTClassBodyDeclaration node, Object data) &lt;br /&gt;
     {&lt;br /&gt;
         getInfo(node);&lt;br /&gt;
         '''if''' (!(node.jjtGetChild(0) '''instanceof''' ASTMethodDeclaration))&lt;br /&gt;
         {&lt;br /&gt;
             '''return null''';&lt;br /&gt;
         } &lt;br /&gt;
         '''this'''.init( );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
When traversing the AST, calls to &amp;lt;tt&amp;gt;getInfo&amp;lt;/tt&amp;gt; are handy if you're unsure of the current node. To see this information printed to standard out, you must set &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Otherwise it continues with a call to &amp;lt;tt&amp;gt;init&amp;lt;/tt&amp;gt; in preparation for the upcoming method. This &amp;lt;tt&amp;gt;init( )&amp;lt;/tt&amp;gt; method is shown in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-2|Example 6-2]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-2. init( ) method'''&lt;br /&gt;
&lt;br /&gt;
 private void init ( )&lt;br /&gt;
 {&lt;br /&gt;
 currMethName = &amp;quot;&amp;quot;;&lt;br /&gt;
     currMethXsVis = 0;&lt;br /&gt;
     currMethParams = new HashMap( );&lt;br /&gt;
     currMethSymptomCode = &amp;quot;&amp;quot;;        &lt;br /&gt;
     currMethExprsToChase = new ArrayList( );    &lt;br /&gt;
     currMethVarsChased = new LinkedList( );&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By returning with a call to &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; of the super class, &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; continues analyzing child nodes. The previous conditional statement guarantees the next visit is to &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
          &lt;br /&gt;
                      '''return super'''.visit(node,data);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
==== ASTMethodDeclaration ====&lt;br /&gt;
&lt;br /&gt;
Here are the relevant nodes for &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt; from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
       MethodDeclaration:(public)&lt;br /&gt;
        ResultType&lt;br /&gt;
        MethodDeclarator:doGet&lt;br /&gt;
         FormalParameters&lt;br /&gt;
          FormalParameter:(package private)&lt;br /&gt;
           Type:&lt;br /&gt;
            Name:HttpServletRequest&lt;br /&gt;
           VariableDeclaratorId:request&lt;br /&gt;
          FormalParameter:(package private)&lt;br /&gt;
           Type:&lt;br /&gt;
            Name:HttpServletResponse&lt;br /&gt;
           VariableDeclaratorId:response&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt; node marks the beginning of the current method in the source:&lt;br /&gt;
&lt;br /&gt;
                      '''public''' Object visit(ASTMethodDeclaration node, Object data) &lt;br /&gt;
 {&lt;br /&gt;
         getInfo(node);&lt;br /&gt;
&lt;br /&gt;
On every visit to this node, &amp;lt;tt&amp;gt;currMethXsVis&amp;lt;/tt&amp;gt; is incremented by 1. If it's the first visit the name of the current method is retrieved:&lt;br /&gt;
&lt;br /&gt;
         currMethXsVis++;&lt;br /&gt;
         &lt;br /&gt;
         '''if''' (currMethXsVis == 1)&lt;br /&gt;
         {&lt;br /&gt;
             currMethName = ((ASTMethodDeclarator)node.jjtGetChild(1)).getImage( );&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;tt&amp;gt;currMethXsVis&amp;lt;/tt&amp;gt; is greater than 1, the class is in the midst of its data-chasing logic—i.e., tracing from symptom code to sources of user input. To avoid confusion, let's assume this is the first visit to the current method and defer an explanation of the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; block until later:&lt;br /&gt;
&lt;br /&gt;
         &lt;br /&gt;
                      '''else''' &lt;br /&gt;
         {    &lt;br /&gt;
             List locVarDecList =                 &lt;br /&gt;
         (ArrayList)node.findChildrenOfType(ASTLocalVariableDeclaration.'''class''');&lt;br /&gt;
             '''for''' (Iterator j = locVarDecList.iterator( ); j.hasNext( );) &lt;br /&gt;
             {&lt;br /&gt;
                 '''if''' (currMethExprsToChase.size( ) &amp;gt; 0)&lt;br /&gt;
                    chkLocVarsForUCI((ASTLocalVariableDeclaration)j.next( ),data);&lt;br /&gt;
                 '''else'''&lt;br /&gt;
                      '''break;'''&lt;br /&gt;
             }&lt;br /&gt;
             &lt;br /&gt;
             '''return null''';&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
As mentioned before, a call to &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; of the super class ensures our class traverses the children of this node:&lt;br /&gt;
&lt;br /&gt;
         return super.visit(node,data);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
==== ASTMethodDeclarator ====&lt;br /&gt;
&lt;br /&gt;
Here are the relevant nodes for &amp;lt;tt&amp;gt;ASTMethodDeclarator&amp;lt;/tt&amp;gt; from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
        MethodDeclarator:doGet&lt;br /&gt;
         FormalParameters&lt;br /&gt;
          FormalParameter:(package private)&lt;br /&gt;
           Type:&lt;br /&gt;
            Name:HttpServletRequest&lt;br /&gt;
           VariableDeclaratorId:request&lt;br /&gt;
          FormalParameter:(package private)&lt;br /&gt;
           Type:&lt;br /&gt;
            Name:HttpServletResponse&lt;br /&gt;
           VariableDeclaratorId:response&lt;br /&gt;
&lt;br /&gt;
On the first visit to &amp;lt;tt&amp;gt;ASTMethodDeclarator&amp;lt;/tt&amp;gt;, visit retrieves a list of method arguments/parameters with a call to &amp;lt;tt&amp;gt;getCurrMethParams&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''public''' Object visit(ASTMethodDeclarator node, Object data) &lt;br /&gt;
     {&lt;br /&gt;
         getInfo(node);&lt;br /&gt;
         &lt;br /&gt;
         '''if''' (currMethXsVis == 1)&lt;br /&gt;
         {&lt;br /&gt;
             getCurrMethParams(node);&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;getCurrMethParams&amp;lt;/tt&amp;gt; begins with a call to &amp;lt;tt&amp;gt;getParameterCount&amp;lt;/tt&amp;gt;, which returns the parameter count for the method in scope. If this number is greater than zero, the code retrieves each parameter represented by the &amp;lt;tt&amp;gt;ASTFormalParameter&amp;lt;/tt&amp;gt; class and stores the name and type as key/value pairs in &amp;lt;tt&amp;gt;currMethParams&amp;lt;/tt&amp;gt;. This list of parameters represents the sources of user-controllable input for the current method.&lt;br /&gt;
&lt;br /&gt;
 private void getCurrMethParams (ASTMethodDeclarator node)&lt;br /&gt;
 { &lt;br /&gt;
  if (node.getParameterCount( ) &amp;gt; 0) &lt;br /&gt;
  {&lt;br /&gt;
   List methodParams = node.findChildrenOfType(ASTFormalParameter.class);&lt;br /&gt;
   for (Iterator i = methodParams.iterator( );i.hasNext( );) &lt;br /&gt;
   {&lt;br /&gt;
    ASTFormalParameter p = (ASTFormalParameter)i.next( );    &lt;br /&gt;
    ASTName pType = (ASTName)p.jjtGetChild(0).jjtGetChild(0);&lt;br /&gt;
    ASTVariableDeclaratorId pName = (ASTVariableDeclaratorId)p.jjtGetChild(1);&lt;br /&gt;
 currMethParams.put(pName.getImage( ),pType.getImage( ));&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
After calling &amp;lt;tt&amp;gt;getCurrMethParams&amp;lt;/tt&amp;gt;, the &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method resumes execution with a call to &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; of the super class:&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''return super'''.visit(node,data);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
With a list of all user-controllable input for the current method, the class could trace each parameter through the AST. The data paths for some of the parameters might lead to symptom code. Methods with a large number of parameters and the likelihood for multiple complex data paths make this approach inefficient. Instead, the class takes a more direct approach by targeting indicators of high-risk code first—i.e., the ''symptomatic code approach'' . It navigates down the AST, visiting &amp;lt;tt&amp;gt;ASTAdditiveExpression&amp;lt;/tt&amp;gt; nodes, because these are indicators of string concatenation and, more specifically, the dynamic building of SQL statements. Further analysis is required to confirm this assumption as well as to chase any expressions concatenated to sources of user-controllable input. A benefit of this approach is that methods without parameters are still analyzed for the presence of dynamic SQL.&lt;br /&gt;
&lt;br /&gt;
Pursuing indicators of high-risk code, as opposed to vulnerabilities themselves, enables the tester (and not the tool) to decide if code is vulnerable and exploitable. The added functionality of tracing from symptom code to user-controllable input is valuable because:&lt;br /&gt;
&lt;br /&gt;
* It saves the tester from having to perform this task manually.&lt;br /&gt;
* It provides information about the data path to further assist the tester when making decisions about the exploitability of a piece of code.&lt;br /&gt;
&lt;br /&gt;
==== ASTAdditiveExpression ====&lt;br /&gt;
&lt;br /&gt;
Here are relevant nodes for &amp;lt;tt&amp;gt;ASTAdditiveExpression&amp;lt;/tt&amp;gt; from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
 AdditiveExpression:+&lt;br /&gt;
   PrimaryExpression&lt;br /&gt;
    PrimaryPrefix&lt;br /&gt;
     Literal:&amp;quot;SELECT * FROM USERS WHERE ID = '&amp;quot;&lt;br /&gt;
   PrimaryExpression&lt;br /&gt;
    PrimaryPrefix&lt;br /&gt;
     Name:id&lt;br /&gt;
   PrimaryExpression&lt;br /&gt;
    PrimaryPrefix&lt;br /&gt;
     Literal:&amp;quot;'&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To hone in on dynamic SQL, the class visits &amp;lt;tt&amp;gt;ASTAdditiveExpression&amp;lt;/tt&amp;gt; nodes and its children &amp;lt;tt&amp;gt;ASTLiteral&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; nodes. The &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; method begins by searching down the AST for &amp;lt;tt&amp;gt;ASTLiteral&amp;lt;/tt&amp;gt; nodes because&amp;lt;tt/&amp;gt;they are likely to contain SQL strings. If it finds any, the code extracts the string stored by the node and passes this value to &amp;lt;tt&amp;gt;isMatch&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
                      '''public''' Object visit(ASTAdditiveExpression node, Object data) {&lt;br /&gt;
         &lt;br /&gt;
         getInfo(node);&lt;br /&gt;
         &lt;br /&gt;
         List literals = node.findChildrenOfType(ASTLiteral.'''class''');&lt;br /&gt;
         &lt;br /&gt;
         '''for '''(Iterator l = literals.iterator( ); l.hasNext( );) &lt;br /&gt;
         {&lt;br /&gt;
             ASTLiteral astLiteral = (ASTLiteral)l.next( );&lt;br /&gt;
             String literal = astLiteral.getImage( );&lt;br /&gt;
             '''if '''(literal != '''null''' &amp;amp;&amp;amp; isMatch(literal))&lt;br /&gt;
             {&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;isMatch&amp;lt;/tt&amp;gt; method relies on the regular expression stored in the &amp;lt;tt&amp;gt;PATTERN&amp;lt;/tt&amp;gt; variable to detect the presence of SQL select statements. The &amp;lt;tt&amp;gt;org.apache.regexp.RE&amp;lt;/tt&amp;gt; class creates the regular expression and matches it against each literal. The method sets the case-independent flag because the case sensitivity of SQL statements often varies with code implementation. A successful match returns the Boolean &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, indicating the existence of dynamic SQL in the source:&lt;br /&gt;
&lt;br /&gt;
 private boolean isMatch(String literal)&lt;br /&gt;
 {&lt;br /&gt;
     '''boolean''' match = '''false''';&lt;br /&gt;
      &lt;br /&gt;
     RE sql = '''new''' RE(PATTERN);&lt;br /&gt;
      &lt;br /&gt;
     sql.setMatchFlags(RE.MATCH_CASEINDEPENDENT);&lt;br /&gt;
         &lt;br /&gt;
     '''return''' sql.match(literal);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;tt&amp;gt;isMatch&amp;lt;/tt&amp;gt; returns &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, the class prepares to add a security violation to the PMD report. The SQL literal is stored for future reference and is added to the message of the current security violation:&lt;br /&gt;
&lt;br /&gt;
                     RuleContext ctx = (RuleContext) data;&lt;br /&gt;
                     currMethSymptomCode = literal;&lt;br /&gt;
                     String msg = MessageFormat.format(getMessage( ), new&lt;br /&gt;
                         Object[]{&amp;quot;SQL select statement detected: &amp;quot; + &lt;br /&gt;
                             currMethSymptomCode});&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;format&amp;lt;/tt&amp;gt; method of &amp;lt;tt&amp;gt;java.text.MessageFormat&amp;lt;/tt&amp;gt; customizes the generic message in ''dynamicsql.xml'' , as in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-3|Example 6-3]], by including the identified symptom code, which in this case is an SQL select statement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-3. Snippet from dynamicsql.xml'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;ruleset&amp;gt;&lt;br /&gt;
    &amp;lt;rule name=&amp;quot;DynSqlSelectStmts&amp;quot; message=&amp;quot;'' {0} ''&amp;quot; class=&amp;quot;net.sourceforge.pmd.&lt;br /&gt;
rules.web.security.DynSqlSelectStmts&amp;quot;&amp;gt;&lt;br /&gt;
...&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The next line of code actually adds the security violation to the PMD report:&lt;br /&gt;
&lt;br /&gt;
 ctx.getReport( ).addRuleViolation(createRuleViolation(ctx, &lt;br /&gt;
    astLiteral.getBeginLine( ), msg));&lt;br /&gt;
&lt;br /&gt;
At this point the class implementation satisfies its primary objective: to identify and report dynamically built SQL statements. The next task is to identify expressions concatenated to the dynamic SQL and determine whether they contain user-controllable input. Examples of these expressions include method parameters, local variables, and methods calls that return data, each a potential source of user-controllable input. Examples from the &amp;lt;tt&amp;gt;doGet&amp;lt;/tt&amp;gt; method ([[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-1|Example 6-1]]) include the following:&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
| Method parameter || &lt;br /&gt;
&lt;br /&gt;
 Request&lt;br /&gt;
|-&lt;br /&gt;
| Local variable || &lt;br /&gt;
&lt;br /&gt;
 id&lt;br /&gt;
|-&lt;br /&gt;
| Method that returns data || &lt;br /&gt;
&lt;br /&gt;
 request.getParameter&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the AST, &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; nodes represent these expressions and are therefore retrieved for analysis:&lt;br /&gt;
&lt;br /&gt;
 List names = node.findChildrenOfType(ASTName.'''class''');&lt;br /&gt;
&lt;br /&gt;
If the list size is greater than zero, the entire list is passed to &amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt; to determine whether any of the expressions are a source of user-controllable input:&lt;br /&gt;
&lt;br /&gt;
                      '''if '''( names.size( ) &amp;gt; 0 )&lt;br /&gt;
 {&lt;br /&gt;
      ArrayList uci = chkForUCI(names);&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt; method, shown in [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-4|Example 6-4]], compares each &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; node to those stored in the &amp;lt;tt&amp;gt;currMethParams&amp;lt;/tt&amp;gt; class variable. Although the nodes can refer to the same instance of an object, they are not always identical expressions. For example, a method parameter named &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt; of type &amp;lt;tt&amp;gt;HttpServletRequest&amp;lt;/tt&amp;gt; could appear in an &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; node in these forms: &amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;request.getQueryString&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;request.getCookies&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;request.getHeader&amp;lt;/tt&amp;gt;, etc. To determine whether these represent sources of user-controllable input, the class could compare them against a list of &amp;lt;tt&amp;gt;HttpServletRequest&amp;lt;/tt&amp;gt; methods known to retrieve user-controllable input from an HTTP request. While exact-match comparisons are ideal for well-known objects (such as &amp;lt;tt&amp;gt;HttpServletRequest&amp;lt;/tt&amp;gt;), the technique falls short when looking for representations of user-controllable input with unfamiliar or custom objects. Instead, the &amp;lt;tt&amp;gt;org.apache.regexp.RE&amp;lt;/tt&amp;gt; regular expression evaluator class is used to compare method parameters (i.e., &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt;) to specific uses of those objects (i.e., &amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt;). The most effective approach is a combination of exact match and regular expression comparisons.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-4. chkForUCI( )'''&lt;br /&gt;
&lt;br /&gt;
 private ArrayList chkForUCI(List names) &lt;br /&gt;
 { &lt;br /&gt;
  ArrayList uci = new ArrayList( );&lt;br /&gt;
  for (Iterator i = names.iterator( );i.hasNext( );) &lt;br /&gt;
  {&lt;br /&gt;
   ASTName name = (ASTName)i.next( );&lt;br /&gt;
   for (Iterator j = currMethParams.keySet( ).iterator( );                                 &lt;br /&gt;
     j.hasNext( );) &lt;br /&gt;
   {&lt;br /&gt;
    String currMethParam = (String)j.next( );&lt;br /&gt;
    RE re = new RE (currMethParam);&lt;br /&gt;
    if ( re.match(name.getImage( )) ) &lt;br /&gt;
    {&lt;br /&gt;
     uci.add(name);&lt;br /&gt;
     break;&lt;br /&gt;
    }&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
   &lt;br /&gt;
  return uci;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt; returns a list of &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; nodes that represent user-controllable input linked to the previously identified SQL select statement. These symptoms point to the existence of a potentially exploitable SQL Injection vulnerability in the source.&lt;br /&gt;
&lt;br /&gt;
Next, the code reports the security violation along with the appended user-controllable input, which is similar to that already described. At this point, the rule has satisfied the objective: to identify and report user-controllable input concatenated to dynamic SQL statements.&lt;br /&gt;
&lt;br /&gt;
                      '''if''' ( ! uci.isEmpty( ) )&lt;br /&gt;
 {&lt;br /&gt;
   // Report the violation&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The following SQL statement would be reported as a potentially exploitable SQL Injection vulnerability.&lt;br /&gt;
&lt;br /&gt;
 String strSql = &amp;quot;select * from user where USER_ID = '&amp;quot; + request.getParameter(&amp;quot;id&amp;quot;) + &amp;quot;'&amp;quot;;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt; returns an empty list, none of the &amp;lt;tt&amp;gt;expressions&amp;lt;/tt&amp;gt; concatenated to the SQL statement represents immediate sources of user-controllable input (for example, &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; in the previous AST). However, these expressions might be on a data path that traces back to user-controllable input. To kick off the data-tracing logic, the code stores the expressions (&amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; nodes) into &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; and revisits the &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt; node (refer to the next section, [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#Data tracing|Section 6.4.2.6]], to step through this code):&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
                      '''else''' &lt;br /&gt;
  {&lt;br /&gt;
    currMethExprsToChase = '''new''' ArrayList(names);&lt;br /&gt;
    visit( (ASTMethodDeclaration)         &lt;br /&gt;
 node.getFirstParentOfType(ASTMethodDeclaration.'''class'''),data);&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; returns, the data-chasing logic is complete for the expressions in &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt;. The calls to &amp;lt;tt&amp;gt;init()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;super.visit&amp;lt;/tt&amp;gt; mark the end of analysis for the method in scope and allow the class to visit the next available &amp;lt;tt&amp;gt;ASTClassBodyDeclaration&amp;lt;/tt&amp;gt; node:&lt;br /&gt;
&lt;br /&gt;
                             &lt;br /&gt;
                      '''this'''.init( );&lt;br /&gt;
                         }&lt;br /&gt;
                     }&lt;br /&gt;
                 }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         &lt;br /&gt;
         '''return''' &lt;br /&gt;
                      '''super'''.visit(node,data);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
==== Data tracing ====&lt;br /&gt;
&lt;br /&gt;
The data-tracing logic presented in this section follows data paths that are linked by consecutive variable initializations. Demonstrating this technique should give you an idea of how to implement data tracing for other potential scenarios.&lt;br /&gt;
&lt;br /&gt;
Here are the relevant nodes from the ''SqlInjectionExample.java'' AST:&lt;br /&gt;
&lt;br /&gt;
  LocalVariableDeclaration:(package private)&lt;br /&gt;
   Type:&lt;br /&gt;
    Name:String&lt;br /&gt;
   VariableDeclarator&lt;br /&gt;
    VariableDeclaratorId:id&lt;br /&gt;
    VariableInitializer&lt;br /&gt;
     Expression&lt;br /&gt;
      PrimaryExpression&lt;br /&gt;
       PrimaryPrefix&lt;br /&gt;
        Name:request.getParameter&lt;br /&gt;
&lt;br /&gt;
Revisiting the ASTMethodDeclaration method diverts execution to the &amp;lt;tt&amp;gt;else&amp;lt;/tt&amp;gt; block, which retrieves a list of &amp;lt;tt&amp;gt;ASTLocalVariableDeclaration&amp;lt;/tt&amp;gt; nodes and passes each to &amp;lt;tt&amp;gt;chkLocVarsForUCI&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''public''' &lt;br /&gt;
                      '''void''' chkLocVarsForUCI(ASTLocalVariableDeclaration node, Object data) &lt;br /&gt;
     {&lt;br /&gt;
&lt;br /&gt;
This method retrieves the name of the local variable declaration from &amp;lt;tt&amp;gt;ASTVariableDeclaratorId&amp;lt;/tt&amp;gt; (&amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; in the AST) and stores it in &amp;lt;tt&amp;gt;varName&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
         ASTVariableDeclarator varDec = (ASTVariableDeclarator)&lt;br /&gt;
                                                  node.jjtGetChild(1);&lt;br /&gt;
         String varName =&lt;br /&gt;
                 ((ASTVariableDeclaratorId)varDec.jjtGetChild(0)).getImage( );&lt;br /&gt;
&lt;br /&gt;
Then the code looks for the expression initializing the local variable (such as &amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt; in the AST). If an &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; node is found, the method stores the expression into &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt;; otherwise, it returns to &amp;lt;tt&amp;gt;visit&amp;lt;/tt&amp;gt; to analyze the remaining &amp;lt;tt&amp;gt;ASTLocalVariableDeclaration&amp;lt;/tt&amp;gt; nodes:&lt;br /&gt;
&lt;br /&gt;
         ASTVariableInitializer varInit =&lt;br /&gt;
                          (ASTVariableInitializer)varDec.jjtGetChild(1);&lt;br /&gt;
         &lt;br /&gt;
         If (varInit.findChildrenOfType(ASTName.'''class''').get(0) instanceof&lt;br /&gt;
                                                             ASTName) &lt;br /&gt;
         {&lt;br /&gt;
             ASTName initExp = (ASTName) &lt;br /&gt;
                            varInit.findChildrenOfType(ASTName.'''class''').get(0);&lt;br /&gt;
         } else {&lt;br /&gt;
             return;&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
Assuming an &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; node is retrieved, the code iterates over &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; (which would contain &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; after visiting &amp;lt;tt&amp;gt;ASTAdditiveExpression&amp;lt;/tt&amp;gt;), comparing each expression to the local variable stored in &amp;lt;tt&amp;gt;varName (id&amp;lt;/tt&amp;gt; in this case&amp;lt;tt&amp;gt;)&amp;lt;/tt&amp;gt;. A match means the class found the initialization of the expression concatenated to the dynamic SQL:&lt;br /&gt;
&lt;br /&gt;
         &lt;br /&gt;
                      '''boolean''' chase = '''false''';&lt;br /&gt;
         '''boolean''' srcOfUCI = '''false''';&lt;br /&gt;
         '''int''' cnt = 0;&lt;br /&gt;
         '''int''' index = 0;&lt;br /&gt;
         '''for''' (Iterator i = currMethExprsToChase.iterator( ); i.hasNext( );) &lt;br /&gt;
         {&lt;br /&gt;
             ASTName currNode = (ASTName)i.next( );&lt;br /&gt;
             '''if''' ( currNode.getImage( ).matches(varName) )&lt;br /&gt;
             {&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;chase&amp;lt;/tt&amp;gt; Boolean variable controls whether additional data tracing is required (i.e., the initializing expression for the local variable is not user-controllable input) and &amp;lt;tt&amp;gt;srcOfUCI&amp;lt;/tt&amp;gt; triggers the reporting code if the initializing expression is a source of user-controllable input. The &amp;lt;tt&amp;gt;cnt&amp;lt;/tt&amp;gt; integer tracks the current position in the &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; array. &amp;lt;tt&amp;gt;index&amp;lt;/tt&amp;gt; stores the value of &amp;lt;tt&amp;gt;cnt&amp;lt;/tt&amp;gt; when either &amp;lt;tt&amp;gt;chase&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;srcOfUCI&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;varName&amp;lt;/tt&amp;gt; matches the name of an expression in &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt;, the variable is added to the end of &amp;lt;tt&amp;gt;currMethVarsChased&amp;lt;/tt&amp;gt; and the initializing expression &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; (&amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt; in this case) is checked as a source of user-controllable input. This implementation of &amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt; is an overloaded version of the previously discussed &amp;lt;tt&amp;gt;chkForUCI&amp;lt;/tt&amp;gt;. It takes a single &amp;lt;tt&amp;gt;ASTName&amp;lt;/tt&amp;gt; node as an argument and returns a string containing the user-controllable input, if the passed-in node matches one in &amp;lt;tt&amp;gt;currMethParams&amp;lt;/tt&amp;gt; (as a result of visiting &amp;lt;tt&amp;gt;ASTMethodDeclarator&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;currMethParams&amp;lt;/tt&amp;gt; would contain the &amp;lt;tt&amp;gt;request&amp;lt;/tt&amp;gt; object and match the initializing expression &amp;lt;tt&amp;gt;request.getParameter&amp;lt;/tt&amp;gt; identifying it as a source of user-controllable input):&lt;br /&gt;
&lt;br /&gt;
                 ((LinkedList)currMethVarsChased).addLast(currNode.getImage( ));&lt;br /&gt;
                 String uci = chkForUCI(initExp);&lt;br /&gt;
&lt;br /&gt;
Given that &amp;lt;tt&amp;gt;uci&amp;lt;/tt&amp;gt; is not &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;srcOfUCI&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, triggering the following block of code that reports &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; as user-controllable input. The &amp;lt;tt&amp;gt;index&amp;lt;/tt&amp;gt; integer stores the current position in the &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; array so that the previously matched expression (&amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; in this case) can be removed, as it no longer needs to be chased. The &amp;lt;tt&amp;gt;break&amp;lt;/tt&amp;gt; keyword exits the loop.&lt;br /&gt;
&lt;br /&gt;
                 &lt;br /&gt;
                      '''if''' (uci != null)&lt;br /&gt;
                 {        &lt;br /&gt;
                     srcOfUCI = '''true''';&lt;br /&gt;
                     index = cnt;&lt;br /&gt;
                     '''break''';&lt;br /&gt;
                 }&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;uci&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt; (i.e., &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; is not a source of user-controllable input), &amp;lt;tt&amp;gt;chase&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, which repeats the data-tracing code for &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt;. Similar to the preceding &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; block, the &amp;lt;tt&amp;gt;index&amp;lt;/tt&amp;gt; integer stores the current position in the &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; array so that its contents can be replaced with &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt;, as this initializing expression now needs to be chased. The &amp;lt;tt&amp;gt;break&amp;lt;/tt&amp;gt; keyword exits the loop.&lt;br /&gt;
&lt;br /&gt;
                 &lt;br /&gt;
                      '''else''' &lt;br /&gt;
                 {&lt;br /&gt;
                     chase = '''true''';&lt;br /&gt;
                     index = cnt;&lt;br /&gt;
                     '''break''';                }&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         cnt++;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;srcOfUCI&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, the local variable initialized with &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; is removed from &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; is added to the end of &amp;lt;tt&amp;gt;currMethVarsChased&amp;lt;/tt&amp;gt;. The initializing expression is also added to the PMD report as a source of user-controllable input, making the previously identified dynamic SQL statement a likely SQL Injection candidate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
Remember, you can verify this vulnerability with access to a live instance of the application.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''if''' (srcOfUCI)&lt;br /&gt;
     {&lt;br /&gt;
         ((ArrayList)currMethExprsToChase).remove(index);&lt;br /&gt;
                 &lt;br /&gt;
         ((LinkedList)currMethVarsChased).addLast(initExp.getImage( ));&lt;br /&gt;
         &lt;br /&gt;
         // Report the violation&lt;br /&gt;
         &lt;br /&gt;
         currMethVarsChased = '''new''' LinkedList( );&lt;br /&gt;
             &lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;chase&amp;lt;/tt&amp;gt; is &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;currMethExprsToChase&amp;lt;/tt&amp;gt; is updated with &amp;lt;tt&amp;gt;initExp&amp;lt;/tt&amp;gt; and the data-chasing logic is repeated with a new call to the &amp;lt;tt&amp;gt;ASTMethodDeclaration&amp;lt;/tt&amp;gt; visit method. This last method call of the data-chasing routine ensures that the rule continues to trace variable initializations until the original source of user-controllable input is found.&lt;br /&gt;
&lt;br /&gt;
     &lt;br /&gt;
                      '''else''' &lt;br /&gt;
                      '''if''' (chase)&lt;br /&gt;
     {&lt;br /&gt;
         ((ArrayList)currMethExprsToChase).remove(index);&lt;br /&gt;
 &lt;br /&gt;
         ((ArrayList)currMethExprsToChase).add(index,initExp);&lt;br /&gt;
 &lt;br /&gt;
         visit( &lt;br /&gt;
             (ASTMethodDeclaration)node.getFirstParentOfType&lt;br /&gt;
                                 (ASTMethodDeclaration.'''class'''), data);&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
To illustrate this new rule in action, [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-FIG-2|Figure 6-2]] shows the report PDM generated when scanning ''SqlInjectionExample.java''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-2. PMD report for SqlInjectionExample.java'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Network Security Tools_I_6_tt316.png|PMD report for SqlInjectionExample.java]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In summary, the &amp;lt;tt&amp;gt;DynSqlSelectStmts&amp;lt;/tt&amp;gt; class is designed to help testers find exploitable SQL Injection vulnerabilities by flagging instances of dynamic SQL and tracing backward to determine whether the symptom code is tied to sources of user-controllable input. The concepts, ideas, and code examples provided in this chapter should supply the groundwork for building future security rules that target a variety of symptom code, regardless of the static code analysis tool you use.&lt;br /&gt;
&lt;br /&gt;
=== SqlInjectionExample.java ===&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-5|Example 6-5]] provides the full source code of the ''SqlInjectionExample.java'' example discussed in this chapter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-5. Source code for SqlInjectionExample.java'''&lt;br /&gt;
&lt;br /&gt;
 import java.io.*;&lt;br /&gt;
 import java.sql.*;&lt;br /&gt;
 import javax.servlet.*;&lt;br /&gt;
 import javax.servlet.http.*;&lt;br /&gt;
 &lt;br /&gt;
 public class SqlInjectionExample extends HttpServlet {&lt;br /&gt;
 &lt;br /&gt;
   public void doGet(HttpServletRequest request, HttpServletResponse response)&lt;br /&gt;
                                throws ServletException, IOException {&lt;br /&gt;
     Connection con = null;&lt;br /&gt;
     Statement stmt = null;&lt;br /&gt;
     ResultSet rs = null;&lt;br /&gt;
 &lt;br /&gt;
     response.setContentType(&amp;quot;text/html&amp;quot;);&lt;br /&gt;
     PrintWriter out = response.getWriter( );&lt;br /&gt;
 &lt;br /&gt;
     String id = request.getParameter(&amp;quot;id&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     try {&lt;br /&gt;
 &lt;br /&gt;
       Class.forName(&amp;quot;oracle.jdbc.driver.OracleDriver&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
       con = DriverManager.getConnection(&lt;br /&gt;
         &amp;quot;jdbc:oracle:thin:@dbhost:1521:ORCL&amp;quot;, &amp;quot;user&amp;quot;, &amp;quot;passwd&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
       String strSql = &amp;quot;SELECT * FROM USERS WHERE ID = '&amp;quot; + id + &amp;quot;'&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
       stmt = con.createStatement( );&lt;br /&gt;
 &lt;br /&gt;
       rs = stmt.executeQuery(strSql);&lt;br /&gt;
 &lt;br /&gt;
       out.println(&amp;quot;&amp;lt;HTML&amp;gt;&amp;lt;HEAD&amp;gt;&amp;lt;TITLE&amp;gt;SqlInjectionExample&amp;lt;/TITLE&amp;gt;&amp;lt;/HEAD&amp;gt;&amp;quot;);&lt;br /&gt;
       out.println(&amp;quot;&amp;lt;BODY&amp;gt;&amp;quot;);&lt;br /&gt;
       while(rs.next( )) {&lt;br /&gt;
         out.println(rs.getString(&amp;quot;firstname&amp;quot;) + &amp;quot;&amp;amp;amp;nbsp;&amp;quot; + rs.getString(&amp;quot;lastname&amp;quot;));&lt;br /&gt;
       }&lt;br /&gt;
       out.println(&amp;quot;&amp;lt;/BODY&amp;gt;&amp;lt;/HTML&amp;gt;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     catch(ClassNotFoundException e) {&lt;br /&gt;
       out.println(&amp;quot;Couldn't load database driver: &amp;quot; + e.getMessage( ));&lt;br /&gt;
     }&lt;br /&gt;
     catch(SQLException e) {&lt;br /&gt;
       out.println(&amp;quot;SQLException caught: &amp;quot; + e.getMessage( ));&lt;br /&gt;
     }&lt;br /&gt;
     finally {&lt;br /&gt;
 &lt;br /&gt;
       try {&lt;br /&gt;
         if (con != null) con.close( );&lt;br /&gt;
       }&lt;br /&gt;
       catch (SQLException ignored) { }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DynSqlSelectStmts.java ===&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-6|Example 6-6]] provides the full source code of the ''DynSqlSelectStmts.java'' example discussed in this chapter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-6. Source code for DynSqlSelectStmts.jav'''&lt;br /&gt;
&lt;br /&gt;
 package net.sourceforge.pmd.rules.web.security;&lt;br /&gt;
 &lt;br /&gt;
 import net.sourceforge.pmd.AbstractRule;&lt;br /&gt;
 import net.sourceforge.pmd.ast.*;&lt;br /&gt;
 import net.sourceforge.pmd.RuleContext;&lt;br /&gt;
 import org.apache.regexp.*;&lt;br /&gt;
 import java.util.*;&lt;br /&gt;
 import java.text.MessageFormat;&lt;br /&gt;
 &lt;br /&gt;
 public class DynSqlSelectStmts extends AbstractRule {&lt;br /&gt;
 &lt;br /&gt;
   private static boolean debug = true;&lt;br /&gt;
 &lt;br /&gt;
   private static final String PATTERN = &amp;quot;select.+from&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
   private String currMethName;&lt;br /&gt;
   private int currMethXsVis;&lt;br /&gt;
   private Map currMethParams;&lt;br /&gt;
   private String currMethSymptomCode;&lt;br /&gt;
   private List currMethExprsToChase;&lt;br /&gt;
   private List currMethVarsChased;&lt;br /&gt;
 &lt;br /&gt;
   private void init ( )&lt;br /&gt;
   {&lt;br /&gt;
     currMethName = &amp;quot;&amp;quot;;&lt;br /&gt;
     currMethXsVis = 0;&lt;br /&gt;
     currMethParams = new HashMap( );&lt;br /&gt;
     currMethSymptomCode = &amp;quot;&amp;quot;;&lt;br /&gt;
     currMethExprsToChase = new ArrayList( );&lt;br /&gt;
     currMethVarsChased = new LinkedList( );&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   public void setDebug (boolean x)&lt;br /&gt;
   {&lt;br /&gt;
     debug = x;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   public void printDebug (String str)&lt;br /&gt;
   {&lt;br /&gt;
     if (debug)&lt;br /&gt;
       System.out.print(str + &amp;quot;\n&amp;quot;);&lt;br /&gt;
   }&lt;br /&gt;
   public Object visit(ASTCompilationUnit node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
     getInfo(node);&lt;br /&gt;
     printDebug(&amp;quot;Rule: &amp;quot; + this.getName( ) + &amp;quot;\n\n&amp;quot;);&lt;br /&gt;
     return super.visit(node,data);&lt;br /&gt;
   }&lt;br /&gt;
   public Object visit(ASTClassBodyDeclaration node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
     getInfo(node);&lt;br /&gt;
 &lt;br /&gt;
     if (!(node.jjtGetChild(0) instanceof ASTMethodDeclaration))&lt;br /&gt;
     {&lt;br /&gt;
       return null;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     this.init( );&lt;br /&gt;
 &lt;br /&gt;
         return super.visit(node,data);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   public Object visit(ASTMethodDeclaration node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
       getInfo(node);&lt;br /&gt;
       currMethXsVis++;&lt;br /&gt;
       printDebug (&amp;quot;Number of visits to &amp;quot; + node.getClass( ).getName( ) + &amp;quot;: &amp;quot; + currMethXsVis +&lt;br /&gt;
         &amp;quot;\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
       if (currMethXsVis == 1)&lt;br /&gt;
       {&lt;br /&gt;
         currMethName = ((ASTMethodDeclarator)node.jjtGetChild(1)).getImage( );&lt;br /&gt;
         printDebug (&amp;quot;Current Method: &amp;quot; + currMethName + &amp;quot;\n&amp;quot;);&lt;br /&gt;
       }&lt;br /&gt;
 &lt;br /&gt;
       else&lt;br /&gt;
       {&lt;br /&gt;
         List locVarDecList = (ArrayList)node.findChildrenOfType&lt;br /&gt;
            (ASTLocalVariableDeclaration.class);&lt;br /&gt;
         for (Iterator j = locVarDecList.iterator( ); j.hasNext( );)&lt;br /&gt;
         {&lt;br /&gt;
           if (currMethExprsToChase.size( ) &amp;gt; 0)&lt;br /&gt;
             chkLocVarsForUCI((ASTLocalVariableDeclaration)j.next( ),data);&lt;br /&gt;
           else&lt;br /&gt;
             break;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         return null;&lt;br /&gt;
       }&lt;br /&gt;
 &lt;br /&gt;
       return super.visit(node,data);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
   public Object visit(ASTMethodDeclarator node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
     getInfo(node);&lt;br /&gt;
 &lt;br /&gt;
     if (currMethXsVis == 1)&lt;br /&gt;
     {&lt;br /&gt;
       getCurrMethParams(node);&lt;br /&gt;
       printCurrMethParams( );&lt;br /&gt;
     }&lt;br /&gt;
     return super.visit(node,data);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
   public Object visit(ASTAdditiveExpression node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
 &lt;br /&gt;
     getInfo(node);&lt;br /&gt;
 &lt;br /&gt;
     List literals = node.findChildrenOfType(ASTLiteral.class);&lt;br /&gt;
 &lt;br /&gt;
       for (Iterator l = literals.iterator( ); l.hasNext( );)&lt;br /&gt;
       {&lt;br /&gt;
         ASTLiteral astLiteral = (ASTLiteral)l.next( );&lt;br /&gt;
         String literal = astLiteral.getImage( );&lt;br /&gt;
         printDebug(&amp;quot;Literal: &amp;quot; + literal + &amp;quot;\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
         if (literal != null &amp;amp;&amp;amp; isMatch(literal))&lt;br /&gt;
         {&lt;br /&gt;
           RuleContext ctx = (RuleContext) data;&lt;br /&gt;
           currMethSymptomCode = literal;&lt;br /&gt;
           String msg = MessageFormat.format(getMessage( ), new Object[]&lt;br /&gt;
             {&amp;quot;SQL select statement detected: &amp;quot; + currMethSymptomCode});&lt;br /&gt;
           printDebug(&amp;quot;Report message: &amp;quot; + msg + &amp;quot;\n&amp;quot;);&lt;br /&gt;
           ctx.getReport( ).addRuleViolation(createRuleViolation&lt;br /&gt;
             (ctx, astLiteral.getBeginLine( ), msg));&lt;br /&gt;
 &lt;br /&gt;
           // Look for expression(s) other than literals appended to SQL&lt;br /&gt;
           List names = (ArrayList) node.findChildrenOfType(ASTName.class);&lt;br /&gt;
           if ( names.size( ) &amp;gt; 0 )&lt;br /&gt;
           {&lt;br /&gt;
             // Check whether the appended expression(s) are UCI&lt;br /&gt;
             List uci = chkForUCI(names);&lt;br /&gt;
             if ( ! uci.isEmpty( ) )&lt;br /&gt;
             {&lt;br /&gt;
               for (Iterator i = uci.iterator( );i.hasNext( );)&lt;br /&gt;
               {&lt;br /&gt;
                 ASTName n = (ASTName)i.next( );&lt;br /&gt;
                 msg = MessageFormat.format(getMessage( ), new Object[]&lt;br /&gt;
                  {&amp;quot;SQL select statement detected with UCI: &amp;quot; + n.getImage( )});&lt;br /&gt;
                 printDebug(&amp;quot;Report message: &amp;quot; + msg + &amp;quot;\n&amp;quot;);&lt;br /&gt;
                 ctx.getReport( ).addRuleViolation&lt;br /&gt;
                   (createRuleViolation(ctx, astLiteral.getBeginLine( ), msg));&lt;br /&gt;
               }&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             /*&lt;br /&gt;
              * Expression(s) appended to SQL are not immediate source of UCI&lt;br /&gt;
              * Re-visit method declaration to begin logic for finding initializer of UCI&lt;br /&gt;
              */&lt;br /&gt;
 &lt;br /&gt;
             else&lt;br /&gt;
             {&lt;br /&gt;
               printDebug (&amp;quot;Expression(s) appended to SQL are not immediate source of &lt;br /&gt;
                 UCI\n\n&amp;quot;);&lt;br /&gt;
               currMethExprsToChase = new ArrayList(names);&lt;br /&gt;
               printDebug(&amp;quot;*** Begin expression chasing routine *** \n\n&amp;quot;);&lt;br /&gt;
               visit( (ASTMethodDeclaration) node.getFirstParentOfType&lt;br /&gt;
                   (ASTMethodDeclaration.class),data);&lt;br /&gt;
               printDebug(&amp;quot;... Exiting from visit - ASTAdditiveExpression ...\n&amp;quot;);&lt;br /&gt;
               printDebug(&amp;quot;*** Returning from expression chasing routine ... &lt;br /&gt;
                         Done with this ASTAdditiveExpression ... any more?? ***\n\n&amp;quot;);&lt;br /&gt;
               this.init( );&lt;br /&gt;
             }&lt;br /&gt;
           }&lt;br /&gt;
 &lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
 &lt;br /&gt;
       return super.visit(node,data);&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   public void chkLocVarsForUCI(ASTLocalVariableDeclaration node, Object data)&lt;br /&gt;
   {&lt;br /&gt;
     getInfo(node);&lt;br /&gt;
 &lt;br /&gt;
     printCurrMethExprsToChase( );&lt;br /&gt;
 &lt;br /&gt;
     ASTVariableDeclarator varDec = (ASTVariableDeclarator)node.jjtGetChild(1);&lt;br /&gt;
     String varName = ((ASTVariableDeclaratorId)varDec.jjtGetChild(0)).getImage( );&lt;br /&gt;
     printDebug(&amp;quot;Local Variable Name: &amp;quot; + varName + &amp;quot;\n&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     ASTVariableInitializer varInit = (ASTVariableInitializer)varDec.jjtGetChild(1);&lt;br /&gt;
 &lt;br /&gt;
     ASTName initExp = null;&lt;br /&gt;
     if (varInit.findChildrenOfType(ASTName.class).size( ) &lt;br /&gt;
         &amp;gt; 0 &amp;amp;&amp;amp; varInit.findChildrenOfType(ASTName.class).get(0) instanceof ASTName)&lt;br /&gt;
     {&lt;br /&gt;
       initExp = (ASTName) varInit.findChildrenOfType(ASTName.class).get(0);&lt;br /&gt;
       printDebug(&amp;quot;Local Variable Initializer: &amp;quot; + initExp.getImage( ) + &amp;quot;\n&amp;quot;);&lt;br /&gt;
     } else {&lt;br /&gt;
       return;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     boolean chase = false;&lt;br /&gt;
     boolean srcOfUCI = false;&lt;br /&gt;
     int cnt = 0;&lt;br /&gt;
     int index = 0;&lt;br /&gt;
     for (Iterator i = currMethExprsToChase.iterator( ); i.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
       ASTName currNode = (ASTName)i.next( );&lt;br /&gt;
       printDebug(&amp;quot;Checking: &amp;quot; + currNode.getImage( ) + &amp;quot;\n&amp;quot;);&lt;br /&gt;
       if ( currNode.getImage( ).matches(varName) )&lt;br /&gt;
       {&lt;br /&gt;
         printDebug(&amp;quot;Loc var: &amp;quot; + varName + &amp;quot; matches '&amp;quot; + currNode.getImage( ) + &amp;quot;', which is&lt;br /&gt;
            an expression we are currently chasing\n&amp;quot;);&lt;br /&gt;
         ((LinkedList)currMethVarsChased).addLast(currNode.getImage( ));&lt;br /&gt;
         String uci = chkForUCI(initExp);&lt;br /&gt;
         if (uci != null)&lt;br /&gt;
         {&lt;br /&gt;
           printDebug(&amp;quot;Initializing expression: &amp;quot; + initExp.getImage( ) + &amp;quot; is a source of UCI:&lt;br /&gt;
             [&amp;quot; + uci + &amp;quot;]\n&amp;quot;);&lt;br /&gt;
           srcOfUCI = true;&lt;br /&gt;
           index = cnt;&lt;br /&gt;
           break;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
           printDebug(&amp;quot;Need to chase the local var initializer: '&amp;quot; &lt;br /&gt;
                     + initExp.getImage( ) + &amp;quot;'\n&amp;quot;);&lt;br /&gt;
           chase = true;&lt;br /&gt;
           index = cnt;&lt;br /&gt;
           break;&lt;br /&gt;
         }&lt;br /&gt;
       }&lt;br /&gt;
       cnt++;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     if (srcOfUCI)&lt;br /&gt;
     {&lt;br /&gt;
       ((ArrayList)currMethExprsToChase).remove(index);&lt;br /&gt;
 &lt;br /&gt;
       /* Add uci - Appending the ASTLiteral node with the expectation that the source&lt;br /&gt;
        * of uci is from HttpServletRequest ( i.e. something like req.getParameter(&amp;quot;id&amp;quot;) ).&lt;br /&gt;
        * This will not always be the case, and so will have to make this &lt;br /&gt;
          a little more generic.&lt;br /&gt;
        */&lt;br /&gt;
 &lt;br /&gt;
       ASTLiteral lit = (ASTLiteral)node.findChildrenOfType(ASTLiteral.class).get(0);&lt;br /&gt;
       ((LinkedList)currMethVarsChased).addLast(initExp.getImage( ) &lt;br /&gt;
         + &amp;quot;(&amp;quot; + lit.getImage( ) + &amp;quot;)&amp;quot;);&lt;br /&gt;
       String uciChased = printCurrMethVarsChased( );&lt;br /&gt;
 &lt;br /&gt;
       RuleContext ctx = (RuleContext) data;&lt;br /&gt;
       String msg = MessageFormat.format(getMessage( ), new Object[]&lt;br /&gt;
         {&amp;quot;SQL select statement detected with UCI: &amp;quot; + uciChased });&lt;br /&gt;
       printDebug(&amp;quot;Report message: &amp;quot; + msg + &amp;quot;\n&amp;quot;);&lt;br /&gt;
       ctx.getReport( ).addRuleViolation(createRuleViolation(ctx, lit.getBeginLine( ), msg));&lt;br /&gt;
       currMethVarsChased = new LinkedList( );&lt;br /&gt;
 &lt;br /&gt;
     } else if (chase)&lt;br /&gt;
     {&lt;br /&gt;
       ((ArrayList)currMethExprsToChase).remove(index);&lt;br /&gt;
 &lt;br /&gt;
       ((ArrayList)currMethExprsToChase).add(index,initExp);&lt;br /&gt;
 &lt;br /&gt;
       visit( (ASTMethodDeclaration)node.getFirstParentOfType&lt;br /&gt;
         (ASTMethodDeclaration.class),data);&lt;br /&gt;
       printDebug(&amp;quot;... Exiting from chkLocVarsForUCI\n&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   public void getInfo (SimpleNode node)&lt;br /&gt;
     {&lt;br /&gt;
     printDebug (&amp;quot;\n====================&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
     Object o = node;&lt;br /&gt;
     Class c = o.getClass( );&lt;br /&gt;
     printDebug (&amp;quot;Class Name: &amp;quot; + c.getName( ));&lt;br /&gt;
 &lt;br /&gt;
     int begLine = node.getBeginLine( );&lt;br /&gt;
     if (begLine != 0)&lt;br /&gt;
     {&lt;br /&gt;
       printDebug(&amp;quot;Line #: &amp;quot; + begLine);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
   private void getCurrMethParams (ASTMethodDeclarator node)&lt;br /&gt;
   {&lt;br /&gt;
    if (node.getParameterCount( ) &amp;gt; 0)&lt;br /&gt;
    {&lt;br /&gt;
     List methodParams = node.findChildrenOfType(ASTFormalParameter.class);&lt;br /&gt;
     for (Iterator i = methodParams.iterator( );i.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
      ASTFormalParameter p = (ASTFormalParameter)i.next( );&lt;br /&gt;
      ASTName pType =   (ASTName)p.jjtGetChild(0).jjtGetChild(0);&lt;br /&gt;
      ASTVariableDeclaratorId pName =   (ASTVariableDeclaratorId)p.jjtGetChild(1);&lt;br /&gt;
      currMethParams.put(pName.getImage( ),pType.getImage( ));&lt;br /&gt;
     }&lt;br /&gt;
    }&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   private void printCurrMethParams ( )&lt;br /&gt;
   {&lt;br /&gt;
     for (Iterator i = currMethParams.keySet( ).iterator( ); i.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
         String key = (String)i.next( );&lt;br /&gt;
         String value = (String)currMethParams.get(key);&lt;br /&gt;
         printDebug (&amp;quot;Param Name: &amp;quot; + key + &amp;quot;, Param Type: &amp;quot; + value);&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   private void printCurrMethExprsToChase ( )&lt;br /&gt;
   {&lt;br /&gt;
     printDebug (&amp;quot;Chasing the following expressions:\n&amp;quot;);&lt;br /&gt;
     for (Iterator i = currMethExprsToChase.iterator( ); i.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
         String value = ((ASTName)i.next( )).getImage( );&lt;br /&gt;
         printDebug (value + &amp;quot;\n&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   private String printCurrMethVarsChased ( )&lt;br /&gt;
   {&lt;br /&gt;
     printDebug (&amp;quot;Chased the following variables to UCI: &amp;quot; + currMethVarsChased.size( ) &lt;br /&gt;
 + &amp;quot;\n&amp;quot;);&lt;br /&gt;
     String str = &amp;quot;&amp;quot;;&lt;br /&gt;
     for (Iterator i = currMethVarsChased.iterator( ); i.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
         String value = (String)i.next( );&lt;br /&gt;
         if (i.hasNext( ))&lt;br /&gt;
         {&lt;br /&gt;
           str = str + (value + &amp;quot; --&amp;gt; &amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
           str = str + value;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     printDebug(str + &amp;quot;\n&amp;quot;);&lt;br /&gt;
     return str;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   private boolean isMatch(String literal)&lt;br /&gt;
   {&lt;br /&gt;
    boolean match = false;&lt;br /&gt;
 &lt;br /&gt;
    RE sql = new RE(PATTERN);&lt;br /&gt;
 &lt;br /&gt;
    sql.setMatchFlags(RE.MATCH_CASEINDEPENDENT);&lt;br /&gt;
 &lt;br /&gt;
    return sql.match(literal);&lt;br /&gt;
 &lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   private List chkForUCI(List names)&lt;br /&gt;
   {&lt;br /&gt;
    List uci = new ArrayList( );&lt;br /&gt;
    for (Iterator i = names.iterator( );i.hasNext( );)&lt;br /&gt;
    {&lt;br /&gt;
     ASTName name = (ASTName)i.next( );&lt;br /&gt;
     for (Iterator j = currMethParams.keySet( ).iterator( ); j.hasNext( );)&lt;br /&gt;
     {&lt;br /&gt;
      String currMethParam = (String)j.next( );&lt;br /&gt;
      RE re = new RE (currMethParam);&lt;br /&gt;
      if ( re.match(name.getImage( )) )&lt;br /&gt;
      {&lt;br /&gt;
       uci.add(name);&lt;br /&gt;
       break;&lt;br /&gt;
      }&lt;br /&gt;
     }&lt;br /&gt;
    }&lt;br /&gt;
    return uci;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
     private String chkForUCI(ASTName name)&lt;br /&gt;
     {&lt;br /&gt;
       for (Iterator j = currMethParams.keySet( ).iterator( );                     &lt;br /&gt;
   j.hasNext( );)&lt;br /&gt;
       {&lt;br /&gt;
        String currMethParam = (String)j.next( );&lt;br /&gt;
        RE re = new RE (currMethParam);&lt;br /&gt;
        if ( re.match(name.getImage( )) )&lt;br /&gt;
        {&lt;br /&gt;
         return currMethParam;&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
       return null;&lt;br /&gt;
      }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dynamicsql.xml ===&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-7|Example 6-7]] provides the rule file that is used with [[Network Security Tools/Modifying and Hacking Security Tools/Extending Code Analysis to the Webroot#networkst-CHP-6-EX-6|Example 6-6]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-6-EX-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-7. Rule file used with DynSqlSelectStmts.java'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ruleset name=&amp;quot;Dynamic SQL Ruleset&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;description&amp;gt;&lt;br /&gt;
This ruleset contains a collection of rules that find instances of potentially &lt;br /&gt;
exploitable dynamic SQL.&lt;br /&gt;
  &amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;rule name=&amp;quot;DynamicSqlSelectStmts&amp;quot;&lt;br /&gt;
        message=&amp;quot;'' {0} ''&amp;quot;&lt;br /&gt;
        class=&amp;quot;net.sourceforge.pmd.rules.web.security.DynSqlSelectStmts&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;description&amp;gt;&lt;br /&gt;
Dynamic SQL or &amp;quot;string building&amp;quot; techniques that rely on unsanitized input values &lt;br /&gt;
are potentially vulnerable to SQL Injection.&lt;br /&gt;
    &amp;lt;/description&amp;gt;&lt;br /&gt;
      &amp;lt;priority&amp;gt;1&amp;lt;/priority&amp;gt;&lt;br /&gt;
    &amp;lt;example&amp;gt;&lt;br /&gt;
&amp;lt;![CDATA[&lt;br /&gt;
&lt;br /&gt;
int id = request.getParameter(&amp;quot;id&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
String sql = &amp;quot;select * from employees where employeeid = &amp;quot; + id;&lt;br /&gt;
&lt;br /&gt;
]]&amp;gt;&lt;br /&gt;
    &amp;lt;/example&amp;gt;&lt;br /&gt;
  &amp;lt;/rule&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- MORE RULES --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ruleset&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notes ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</description>
			<pubDate>Tue, 11 Mar 2008 21:39:00 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Extending_Code_Analysis_to_the_Webroot</comments>		</item>
	</channel>
</rss>