1 /*
2 * Copyright (c) 2007, Ounce Labs, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the <organization> nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY OUNCE LABS, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL OUNCE LABS, INC. BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 package org.codehaus.mojo.ounce;
29
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Set;
33
34 import org.apache.maven.plugin.MojoExecutionException;
35 import org.apache.maven.plugin.MojoFailureException;
36 import org.codehaus.mojo.ounce.core.OunceCore;
37 import org.codehaus.mojo.ounce.core.OunceCoreException;
38 import org.codehaus.mojo.ounce.utils.Utils;
39 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
40 import org.codehaus.plexus.util.StringUtils;
41
42 /**
43 * This mojo allows an on demand scan of an application and the optional publishing of the results.
44 *
45 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
46 * @goal scan
47 * @aggregator
48 * @execute lifecycle="scan" phase="package"
49 */
50 public class ScanMojo
51 extends AbstractOunceMojo
52 {
53
54 /**
55 * The location of the application file (.paf) to scan.
56 *
57 * @parameter expression="${ounce.applicationFile}" default-value="${basedir}/${project.artifactId}.paf"
58 */
59 String applicationFile;
60
61 /**
62 * A name to help identify the assessment.
63 *
64 * @parameter expression="${project.name}-${project.version}"
65 */
66 String assessmentName;
67
68 /**
69 * A filename to which to save the assessment. <br/> If filename is not specified, Ounce/Maven generates a name
70 * based on the application name and timestamp and saves it to the applicationŐs working directory.
71 *
72 * @parameter expression="${ounce.assessmentOutput}"
73 */
74 String assessmentOutput;
75
76 /**
77 * A short string to help identify the corresponding entries in the ounceauto log file.
78 *
79 * @parameter expression="${ounce.caller}"
80 */
81 String caller;
82
83 /**
84 * Generates an Ounce report of the specified type, including findings reports, SmartAudit Reports, and, if
85 * available, custom reports. Ounce/Maven generates a report for this assessment after the scan completes. <br/> The
86 * following report types are included: Findings, Findings By CWE, Findings By API, Findings By Classification,
87 * Findings By File, Findings By Type, Findings By Bundle, OWASP Top Ten, PCI Data Security Standard, Ounce Software
88 * Security Profile, or OWASP Top Ten 2007 <br/> If you specify reportType, then reportOutputType and
89 * reportOutputPath are required.
90 *
91 * @parameter expression="${ounce.reportType}"
92 */
93 String reportType;
94
95 /**
96 * The output to generate for the report specified in reportType. Required with reportType. Output type may be html,
97 * zip, pdf-summary, pdf-detailed, pdf-comprehensive, or pdf-annotated.
98 *
99 * @parameter expression="${ounce.reportOutputType}"
100 */
101 String reportOutputType;
102
103 /**
104 * The path to which to write the report specified in reportType. Required with reportType.
105 *
106 * @parameter expression="${ounce.reportOutputPath}"
107 */
108 String reportOutputPath;
109
110 /**
111 * Number of lines of source code to include in the report before each finding.
112 *
113 * @parameter expression="${ounce.includeSrcBefore}"
114 */
115 int includeSrcBefore = -1;
116
117 /**
118 * Number of lines of source code to include in the report after each finding.
119 *
120 * @parameter expression="${ounce.includeSrcAfter}"
121 */
122 int includeSrcAfter = -1;
123
124 /**
125 * Automatically publish the assessment following the completion of the scan.
126 *
127 * @parameter expression="${ounce.publish}" default-value="false"
128 */
129 boolean publish;
130
131 /**
132 * The location of the Ounce client installation directory if the Ounce client is not on the path.
133 *
134 * @parameter expression="${ounce.installDir}"
135 */
136 String installDir;
137
138 /**
139 * Forces the goal to wait until the scan finishes, thus blocking the Maven build. This is useful if the scan is
140 * being performed from the report mojo as part of integration with the site target and the site is getting
141 * deployed.
142 *
143 * @parameter expression="${ounce.wait}" default-value="false"
144 */
145 boolean waitForScan;
146
147 /**
148 * This is a static variable used to persist the cached results across plugin invocations.
149 */
150 protected static Set cache = new HashSet();
151
152 // private List projects;
153 /*
154 * (non-Javadoc)
155 *
156 * @see org.apache.maven.plugin.Mojo#execute()
157 */
158 public void execute()
159 throws MojoExecutionException, MojoFailureException
160 {
161
162 if ( StringUtils.isEmpty( applicationFile ) )
163 {
164 throw new MojoExecutionException( "\'applicationFile\' must be defined." );
165 }
166
167 // check my cache to see if this particular set of params has already been scanned.
168 if ( shouldExecute() )
169 {
170 try
171 {
172 if ( includeSrcAfter != -1 || includeSrcBefore != -1 )
173 {
174 if ( options == null )
175 {
176 options = new HashMap();
177 }
178 options.put( "includeSrcAfter", new Integer( includeSrcAfter ) );
179 options.put( "includeSrcBefore", new Integer( includeSrcBefore ) );
180 }
181 OunceCore core = getCore();
182 core.scan( Utils.convertToVariablePath( applicationFile, pathVariableMap ), assessmentName,
183 assessmentOutput, caller, reportType, reportOutputType, reportOutputPath, publish,
184 this.options, this.installDir, waitForScan, getLog() );
185 }
186 catch ( ComponentLookupException e )
187 {
188 throw new MojoExecutionException( "Unable to lookup the core interface for hint: " + coreHint, e );
189 }
190 catch ( OunceCoreException e )
191 {
192 throw new MojoExecutionException( "Nested Ouncecore exception: " + e.getLocalizedMessage(), e );
193 }
194 }
195 else
196 {
197 this.getLog().info(
198 "Skipping Scan because these same parameters where already used in a scan for this project during this build. (build was probably forked)" );
199 }
200 }
201
202 /**
203 * This method checks the cache to see if the scan should be run. It is used to avoid multiple invocations of scan
204 * in the instance of a forked build.
205 *
206 * @return
207 */
208 protected boolean shouldExecute()
209 {
210 // get the hash and try to add it.
211 // if it was added, then we need to execute, otherwise it was already there
212 // and we don't.
213 return ( cache.add( getParameterHash() ) );
214 }
215
216 /**
217 * This method returns a hash of the parameters that may influence scan results. The result of this is used to
218 * determine if a scan should be rerun within the same build lifetime. If any parameters change, then the scan will
219 * continue. This allows multiple executions to be defined with slightly different parameters.
220 *
221 * @return String of hashCodes
222 */
223 protected String getParameterHash()
224 {
225 StringBuffer buf = new StringBuffer();
226 buf.append( getSafeHash( this.applicationFile ) );
227 buf.append( "-" );
228 buf.append( getSafeHash( this.assessmentOutput ) );
229 buf.append( "-" );
230 buf.append( getSafeHash( this.caller ) );
231 buf.append( "-" );
232 buf.append( getSafeHash( this.pathVariableMap ) );
233 buf.append( "-" );
234 buf.append( getSafeHash( this.reportOutputPath ) );
235 buf.append( "-" );
236 buf.append( getSafeHash( this.reportOutputType ) );
237 buf.append( "-" );
238 buf.append( getSafeHash( this.reportType ) );
239 this.getLog().debug( "Parameter Hash: " + buf.toString() );
240 return buf.toString();
241 }
242
243 /**
244 * Simple helper to handle null parameters for hash checking.
245 *
246 * @param o
247 * @return
248 */
249 private final int getSafeHash( Object o )
250 {
251 if ( o != null )
252 {
253 return o.hashCode();
254 }
255 else
256 {
257 return 0;
258 }
259 }
260
261 }