Skip to content

Commit 7c459eb

Browse files
committed
1 parent c16c4cd commit 7c459eb

File tree

2 files changed

+363
-0
lines changed

2 files changed

+363
-0
lines changed

build.gradle

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ allprojects {
9595
}
9696
}
9797
}
98+
// this will apply the javadoc fix tool to all generated javadocs
99+
// we use it to make sure that the javadocs are not vulnerable independently of the JDK used to build
100+
[Javadoc, Groovydoc].each {
101+
tasks.withType(it).all {
102+
doLast {
103+
def javadocFix = new JavadocFixTool()
104+
javadocFix.recursive = true
105+
javadocFix.doPatch = true
106+
javadocFix.searchAndPatch(destinationDir)
107+
}
108+
}
109+
110+
}
98111
}
99112

100113
apply from: "gradle/idea.gradle"
Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
/*
2+
* Copyright (c) 2013 Oracle and/or its affiliates.
3+
* All rights reserved. Use is subject to license terms.
4+
*
5+
* License Agreement
6+
*
7+
* PLEASE READ THE FOLLOWING LICENSE TERMS CAREFULLY BEFORE USING THE
8+
* ACCOMPANYING PROGRAM. THESE TERMS CONSTITUTE A LEGAL AGREEMENT BETWEEN
9+
* YOU AND US.
10+
*
11+
* "Oracle" refers to Oracle America, Inc., for and on behalf of itself and its
12+
* subsidiaries and affiliates under common control. "We," "us," and "our"
13+
* refers to Oracle and any Program contributors. "You" and "your" refers to
14+
* the individual or entity that wishes to use the Program. "Program" refers to
15+
* the Java API Documentation Updater Tool, Copyright (c) 2013, Oracle America,
16+
* Inc., and updates or error corrections provided by Oracle or contributors.
17+
*
18+
* WARNING:
19+
* The Program will analyze directory information on your computer
20+
* system and may modify software components on such computer system. You
21+
* should only use the Program on computer systems that you maintain sufficient
22+
* rights to update software components.
23+
*
24+
* If your computer system is owned by a person or entity other than you,
25+
* you should check with such person or entity before using the Program.
26+
*
27+
* It is possible that you may lose some software functionality, and make
28+
* Java API Documentation pages unusable on your computer system after you use
29+
* the Program to update software components.
30+
*
31+
* License Rights and Obligations
32+
* We grant you a perpetual, nonexclusive, limited license to use, modify and
33+
* distribute the Program in binary and/or source code form, only for the
34+
* purpose of analyzing the directory structure of your computer system and
35+
* updating Java API Documentation files. If you distribute the Program, in
36+
* either or both binary or source form, including as modified by you, you
37+
* shall include this License Agreement ("Agreement") with your distribution.
38+
*
39+
* All rights not expressly granted above are hereby reserved. If you want to
40+
* use the Program for any purpose other than as permitted under this
41+
* Agreement, you must obtain a valid license permitting such use from Oracle.
42+
* Neither the name of Oracle nor the names of any Program contributors may be
43+
* used to endorse or promote products derived from this software without
44+
* specific prior written permission.
45+
*
46+
* Ownership and Restrictions
47+
* We retain all ownership and intellectual property rights in the Program as
48+
* provided by us. You retain all ownership and intellectual property rights
49+
* in your modifications.
50+
*
51+
* Export
52+
* You agree to comply fully with export laws and regulations of the United
53+
* States and any other applicable export laws ("Export Laws") to assure that
54+
* neither the Program nor any direct products thereof are: (1) exported,
55+
* directly or indirectly, in violation of this Agreement or Export Laws; or
56+
* (2) used for any purposes prohibited by the Export Laws, including, without
57+
* limitation, nuclear, chemical, or biological weapons proliferation, or
58+
* development of missile technology.
59+
*
60+
* Disclaimer of Warranty and Limitation of Liability
61+
* THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. USE AT YOUR
62+
* OWN RISK. WE FURTHER DISCLAIM ALL WARRANTIES, EXPRESS AND IMPLIED,
63+
* INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF MERCHANTABILITY,
64+
* FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
65+
*
66+
* IN NO EVENT SHALL WE BE LIABLE FOR ANY INDIRECT, DIRECT, INCIDENTAL,
67+
* SPECIAL, PUNITIVE OR CONSEQUENTIAL DAMAGES, OR DAMAGES FOR LOSS OF PROFITS,
68+
* REVENUE, DATA OR DATA USE, INCURRED BY YOU OR ANY THIRD PARTY, WHETHER IN AN
69+
* ACTION IN CONTRACT OR TORT, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY
70+
* OF SUCH DAMAGES. ORACLE SHALL HAVE NO LIABILITY FOR MODIFICATIONS MADE BY
71+
* YOU OR ANY THIRD PARTY.
72+
*
73+
* Entire Agreement
74+
* You agree that this Agreement is the complete agreement for the Program, and
75+
* this Agreement supersedes all prior or contemporaneous agreements or
76+
* representations. If any term of this Agreement is found to be invalid or
77+
* unenforceable, the remaining provisions will remain effective. This
78+
* Agreement is governed by the substantive and procedural laws of California.
79+
* You and Oracle agree to submit to the exclusive jurisdiction of, and venue
80+
* in, the courts of San Francisco or Santa Clara counties in California in
81+
* any dispute between you and Oracle arising out of or relating to this
82+
* Agreement.
83+
*
84+
* Last updated: 14 June 2013
85+
*/
86+
import java.io.*;
87+
88+
/*
89+
* Tool for finding and addressing files related to CVE-2013-1571.
90+
* See README file for details.
91+
*/
92+
public class JavadocFixTool {
93+
// Usual suspects
94+
private final static String[] fileNames = {"index.html",
95+
"index.htm",
96+
"toc.html",
97+
"toc.htm"};
98+
99+
// If we locate this function but not validURL - we are in trouble
100+
private final String patchString = "function loadFrames() {";
101+
// Main fix - should be inserted before the loadFrames() function alongside
102+
// the code that calls this function
103+
private final static String[] patchData =
104+
{" if (targetPage != \"\" && !validURL(targetPage))",
105+
" targetPage = \"undefined\";",
106+
" function validURL(url) {",
107+
" var pos = url.indexOf(\".html\");",
108+
" if (pos == -1 || pos != url.length - 5)",
109+
" return false;",
110+
" var allowNumber = false;",
111+
" var allowSep = false;",
112+
" var seenDot = false;",
113+
" for (var i = 0; i < url.length - 5; i++) {",
114+
" var ch = url.charAt(i);",
115+
" if ('a' <= ch && ch <= 'z' ||",
116+
" 'A' <= ch && ch <= 'Z' ||",
117+
" ch == '$' ||",
118+
" ch == '_') {",
119+
" allowNumber = true;",
120+
" allowSep = true;",
121+
" } else if ('0' <= ch && ch <= '9'",
122+
" || ch == '-') {",
123+
" if (!allowNumber)",
124+
" return false;",
125+
" } else if (ch == '/' || ch == '.') {",
126+
" if (!allowSep)",
127+
" return false;",
128+
" allowNumber = false;",
129+
" allowSep = false;",
130+
" if (ch == '.')",
131+
" seenDot = true;",
132+
" if (ch == '/' && seenDot)",
133+
" return false;",
134+
" } else {",
135+
" return false;",
136+
" }",
137+
" }",
138+
" return true;",
139+
" }",
140+
" function loadFrames() {"};
141+
142+
private final String quickFixString = "if (!(url.indexOf(\".html\") == url.length - 5))";
143+
private final String[] quickFix = {" var pos = url.indexOf(\".html\");",
144+
" if (pos == -1 || pos != url.length - 5)"};
145+
private static String readme = null;
146+
private static String version = "Java Documentation Updater Tool version 1.2 06/14/2013\n";
147+
148+
private static boolean doPatch = true; // By default patch file
149+
private static boolean recursive = false; // By default only look in the folder in parameter
150+
151+
public static void main(String[] args) {
152+
System.out.println(version);
153+
154+
if (args.length < 1) {
155+
// No arguments - lazily initialize readme, print readme and usage
156+
initReadme();
157+
if (readme != null) {
158+
System.out.println(readme);
159+
}
160+
printUsage(System.out);
161+
return;
162+
}
163+
164+
// Last argument should be a path to the document root
165+
String name = args[args.length-1];
166+
167+
// Analyze the rest of parameters
168+
for (int i = 0 ; i < args.length -1; i++) {
169+
if ("-R".equalsIgnoreCase(args[i])) {
170+
recursive = true;
171+
} else if ("-C".equalsIgnoreCase(args[i])) {
172+
doPatch = false;
173+
} else {
174+
System.err.println("Unknown option passed: "+args[i]);
175+
printUsage(System.err);
176+
return;
177+
}
178+
}
179+
new JavadocFixTool().proceed(name);
180+
}
181+
182+
/*
183+
* Print usage information into the provided PrintStream
184+
* @param out PrintStream to write usage information
185+
*/
186+
public static void printUsage(PrintStream out) {
187+
out.println("Usage: java -jar JavadocPatchTool.jar [-R] [-C] <Path to Javadoc root>");
188+
out.println(" -R : Proceed recursively starting from given folder");
189+
out.println(" -C : Check only - program will find vulnerable files and print their full paths");
190+
}
191+
192+
/*
193+
* Lazily initialize the readme document, reading it from README file inside the jar
194+
*/
195+
public static void initReadme() {
196+
try {
197+
InputStream readmeStream = JavadocFixTool.class.getResourceAsStream("/README");
198+
if (readmeStream != null) {
199+
BufferedReader readmeReader = new BufferedReader(new InputStreamReader(readmeStream));
200+
StringBuilder readmeBuilder = new StringBuilder();
201+
String s;
202+
while ((s = readmeReader.readLine()) != null) {
203+
readmeBuilder.append(s);
204+
readmeBuilder.append("\n");
205+
}
206+
readme = readmeBuilder.toString();
207+
}
208+
} catch (IOException ignore) {} // Ignore exception - readme not initialized
209+
}
210+
211+
/*
212+
* Main procedure - proceed with the searching and/or fixing depending on
213+
* the command line parameters
214+
* @param name Path to the document root
215+
*/
216+
public void proceed(String name) {
217+
try {
218+
File folder = new File(name);
219+
if (folder.exists() && folder.isDirectory() && folder.canRead()) {
220+
searchAndPatch(folder);
221+
} else {
222+
System.err.println("Invalid folder in parameter \""+name+"\"");
223+
printUsage(System.err);
224+
}
225+
} catch (Exception ignored) {} // Die silently
226+
}
227+
228+
/*
229+
* Find all the files that match the list given in the fileNames array.
230+
* If file found attempt to patch it.
231+
* If global parameter recursive is set to true attempt to go into the enclosed subfolders
232+
* otherwise only patch said files in the folder directly pointed in parameter.
233+
*/
234+
public void searchAndPatch(File folder) {
235+
if (folder == null || !folder.isDirectory() || folder.list() == null) {
236+
// Silently return
237+
return;
238+
}
239+
240+
for (File file : folder.listFiles()) {
241+
if (file.isDirectory()) {
242+
if(recursive) {
243+
searchAndPatch(file);
244+
}
245+
continue;
246+
}
247+
String name = file.getName();
248+
for (String s : fileNames) {
249+
if (s.equalsIgnoreCase(name)) {
250+
try {
251+
applyPatch(file, folder);
252+
} catch (Exception ex) {
253+
String filePath;
254+
try {
255+
filePath = file.getCanonicalPath();
256+
} catch (IOException ioe) {
257+
System.err.println("Can not resolve path to "+file.getName()+" in folder "+folder.getName());
258+
continue;
259+
}
260+
System.err.println("Patch failed on: "+filePath+" due to the "+ex);
261+
}
262+
}
263+
}
264+
}
265+
}
266+
267+
/*
268+
* Try to apply patch to the single file in the specific folder
269+
* If global parameter doPatch is false we should only print the location of the vulnerable html file
270+
* and return
271+
*/
272+
public void applyPatch(File file, File currentFolder) throws Exception {
273+
FileInputStream fis = new FileInputStream(file);
274+
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
275+
String line;
276+
String failedString = patchString;
277+
String[] patch = patchData;
278+
// Attempt to look if file is vulnerable
279+
for (int i = 0 ; i < 80 ; i++) { // Check first 80 lines - if there is no signature it is not our file
280+
line = br.readLine();
281+
if (line == null) {
282+
// File less than 80 lines long, no signature encountered
283+
return;
284+
}
285+
if (line.trim().equals("function validURL(url) {")) { // Already patched
286+
failedString = null;
287+
patch = null;
288+
continue;
289+
}
290+
if (line.trim().equals(quickFixString)) { // The patch had famous 2-letter bug, update it
291+
failedString = quickFixString;
292+
patch = quickFix;
293+
continue;
294+
}
295+
if (line.trim().equals("function loadFrames() {")) {
296+
fis.close(); // It should not interfere with the file renaming process
297+
if (failedString != null) {
298+
// Vulnerable file
299+
if (!doPatch) { // Report and return
300+
System.out.println("Vulnerable file found: "+file.getCanonicalPath());
301+
} else {
302+
replaceStringInFile(currentFolder, file, failedString, patch);
303+
}
304+
}
305+
return;
306+
}
307+
}
308+
}
309+
310+
/*
311+
* Replace one line in the given file in the given folder with the lines given
312+
* @param folder Folder in which file should be created
313+
* @param file Original file to patch
314+
* @param template Trimmed String with the pattern we are have to find
315+
* @param replacement Array of String that has to be written in the place of first line matching the template
316+
*/
317+
public void replaceStringInFile(File folder, File file, String template, String[] replacement)
318+
throws IOException {
319+
System.out.println("Patching file: "+file.getCanonicalPath());
320+
String name = file.getName();
321+
File origFile = new File(folder, name+".orig");
322+
file.renameTo(origFile);
323+
File temporaryFile = new File(folder, name+".tmp");
324+
if (temporaryFile.exists()) {
325+
temporaryFile.delete();
326+
}
327+
temporaryFile.createNewFile();
328+
String line;
329+
FileInputStream fis = new FileInputStream(origFile);
330+
PrintWriter pw = new PrintWriter(temporaryFile);
331+
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
332+
while ((line = br.readLine()) != null) {
333+
if (line.trim().equals(template)) {
334+
for (String s : replacement) {
335+
pw.println(s);
336+
}
337+
} else {
338+
pw.println(line);
339+
}
340+
}
341+
pw.flush();
342+
pw.close();
343+
if (!temporaryFile.renameTo(new File(folder, name))) {
344+
throw new IOException("Unable to rename file in folder "+folder.getName()+
345+
" from \""+temporaryFile.getName()+"\" into \""+name +
346+
"\n Original file saved as "+origFile.getName());
347+
}
348+
origFile.delete();
349+
}
350+
}

0 commit comments

Comments
 (0)