Skip to content

Commit b261d01

Browse files
author
arshan.dabirsiaghi
committed
- switched response buffering from ByteArrayOutputStream to RandomAccessFile due to BAOS being awful at that type of thing (multiple files)
- refactored early failing code in egress rules to be smarter (ReplaceContentRule.java and DetectOutboundContentRule.java) - fixed dump() in FileBasedAuthenticator.java to handle IndexOutOfBoundsException when there are no entries (this was breaking a few of the WAF test cases)
1 parent abb15f1 commit b261d01

6 files changed

Lines changed: 115 additions & 33 deletions

File tree

src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,11 @@ private String dump(Collection<String> c) {
761761
for (String s : c) {
762762
sb.append(s).append(",");
763763
}
764-
return sb.toString().substring(0, sb.length() - 1);
764+
if ( c.size() > 0) {
765+
return sb.toString().substring(0, sb.length() - 1);
766+
}
767+
return "";
768+
765769
}
766770

767771
/**

src/main/java/org/owasp/esapi/waf/internal/InterceptingHTTPServletRequest.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
*/
1616
package org.owasp.esapi.waf.internal;
1717

18-
import java.io.ByteArrayOutputStream;
18+
import java.io.File;
1919
import java.io.IOException;
2020
import java.io.InputStream;
21+
import java.io.RandomAccessFile;
2122
import java.util.Enumeration;
2223
import java.util.Vector;
2324

@@ -89,17 +90,22 @@ public InterceptingHTTPServletRequest(HttpServletRequest request) throws UploadT
8990
* regular form field. Our job is to stream it
9091
* to make sure it's not too big.
9192
*/
93+
94+
//oew=owasp esapi waf, mpc=multipart content
9295

93-
ByteArrayOutputStream baos = new ByteArrayOutputStream(request.getContentLength());
96+
RandomAccessFile raf = new RandomAccessFile( File.createTempFile("oew-"+item.getFieldName(),"mpc"), "r");
97+
9498
byte buffer[] = new byte[CHUNKED_BUFFER_SIZE];
9599

96100
int size = 0;
97101
int len = 0;
98102

99-
while ( len != -1 || size <= AppGuardianConfiguration.MAX_FILE_SIZE ) {
103+
while ( len != -1 && size <= AppGuardianConfiguration.MAX_FILE_SIZE ) {
100104
len = stream.read(buffer, 0, CHUNKED_BUFFER_SIZE);
101-
size += len;
102-
baos.write(stream.read());
105+
if ( len != -1 ) {
106+
size += len;
107+
raf.write(buffer,0,len);
108+
}
103109
}
104110

105111
if ( size > AppGuardianConfiguration.MAX_FILE_SIZE) {
@@ -108,6 +114,8 @@ public InterceptingHTTPServletRequest(HttpServletRequest request) throws UploadT
108114
}
109115

110116
}
117+
118+
request.getInputStream().reset();
111119
}
112120

113121
}
@@ -120,6 +128,7 @@ public String getDictionaryParameter(String s) {
120128
return p.getValue();
121129
}
122130
}
131+
123132
return null;
124133
}
125134

src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616
package org.owasp.esapi.waf.internal;
1717

18-
import java.io.ByteArrayOutputStream;
18+
import java.io.File;
19+
import java.io.FileNotFoundException;
1920
import java.io.IOException;
21+
import java.io.RandomAccessFile;
2022

2123
import javax.servlet.ServletOutputStream;
2224

@@ -31,76 +33,126 @@
3133

3234
public class InterceptingServletOutputStream extends ServletOutputStream {
3335

36+
private static final int FLUSH_BLOCK_SIZE = 1024;
3437
private ServletOutputStream os;
35-
private ByteArrayOutputStream bos;
3638
private boolean buffering;
37-
38-
public InterceptingServletOutputStream(ServletOutputStream os, boolean buffered) {
39+
private boolean committed;
40+
private boolean closed;
41+
42+
private RandomAccessFile out;
43+
44+
public InterceptingServletOutputStream(ServletOutputStream os, boolean buffered) throws FileNotFoundException, IOException {
3945
super();
4046
this.os = os;
4147
this.buffering = buffered;
42-
this.bos = new ByteArrayOutputStream();
48+
this.committed = false;
49+
this.closed = false;
50+
51+
/*
52+
* Creating a RandomAccessFile to keep track of output generated. I made
53+
* the prefix and suffix small for less processing. The "oew" is intended
54+
* to stand for "OWASP ESAPI WAF" and the "hop" for HTTP output.
55+
*/
56+
this.out = new RandomAccessFile ( File.createTempFile("oew", ".hop"), "rw" );
4357
}
4458

45-
public void reset() {
46-
bos.reset();
59+
public void reset() throws IOException {
60+
out.setLength(0L);
4761
}
4862

49-
public byte[] getResponseBytes() {
50-
return bos.toByteArray();
63+
public byte[] getResponseBytes() throws IOException {
64+
65+
byte[] buffer = new byte[(int) out.length()];
66+
out.seek(0);
67+
out.read(buffer, 0, (int)out.length());
68+
out.seek(out.length());
69+
return buffer;
70+
5171
}
5272

5373
public void setResponseBytes(byte[] responseBytes) throws IOException {
54-
if ( ! buffering && bos.toByteArray().length > 0 ) {
74+
75+
if ( ! buffering && out.length() > 0 ) {
5576
throw new IOException("Already committed response because not currently buffering");
5677
}
5778

58-
bos = new ByteArrayOutputStream();
59-
bos.write(responseBytes);
79+
out.setLength(0L);
80+
out.write(responseBytes);
6081
}
6182

6283
public void write(int i) throws IOException {
6384
if (!buffering) {
6485
os.write(i);
6586
}
66-
bos.write(i);
87+
out.write(i);
6788
}
6889

6990
public void write(byte[] b) throws IOException {
7091
if (!buffering) {
7192
os.write(b, 0, b.length);
7293
}
73-
bos.write(b, 0, b.length);
94+
out.write(b, 0, b.length);
7495
}
7596

7697
public void write(byte[] b, int off, int len) throws IOException {
7798
if (!buffering) {
7899
os.write(b, off, len);
79100
}
80-
bos.write(b, off, len);
101+
out.write(b, off, len);
81102
}
82103

83104
public void flush() throws IOException {
105+
84106
if (buffering) {
85-
os.write(bos.toByteArray());
107+
108+
synchronized(out) {
109+
110+
out.seek(0);
111+
112+
byte[] buff = new byte[FLUSH_BLOCK_SIZE];
113+
114+
for(int i=0;i<out.length();) {
115+
116+
long currentPos = out.getFilePointer();
117+
long totalSize = out.length();
118+
int amountToWrite = FLUSH_BLOCK_SIZE;
119+
120+
if ( (totalSize - currentPos) < FLUSH_BLOCK_SIZE ) {
121+
amountToWrite = (int) (totalSize - currentPos);
122+
}
123+
124+
out.read(buff, 0, (int)amountToWrite);
125+
String s = new String(buff);System.out.println(s);
126+
os.write(buff,0,amountToWrite);
127+
128+
i+=amountToWrite;
129+
130+
}
131+
132+
out.setLength(0);
133+
134+
}
86135
}
87136

88-
bos.reset();
89137
}
90138

91139
public void commit() throws IOException {
92-
if (!buffering) {
140+
141+
if (!buffering) { // || committed || closed
93142
return;
143+
} else {
144+
flush();
94145
}
95-
bos.writeTo(os);
96-
os.close();
97-
bos.close();
146+
committed = true;
98147
}
99148

100149
public void close() throws IOException {
150+
101151
if (!buffering) {
102152
os.close();
103153
}
154+
closed = true;
155+
104156
}
105157

106158
}

src/main/java/org/owasp/esapi/waf/rules/DetectOutboundContentRule.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.owasp.esapi.waf.rules;
1717

18+
import java.io.IOException;
1819
import java.io.UnsupportedEncodingException;
1920
import java.util.regex.Pattern;
2021

@@ -44,8 +45,6 @@ public Action check(HttpServletRequest request,
4445
InterceptingHTTPServletResponse response,
4546
HttpServletResponse httpResponse) {
4647

47-
byte[] bytes = response.getInterceptingServletOutputStream().getResponseBytes();
48-
4948
/*
5049
* Early fail: if URL doesn't match.
5150
*/
@@ -82,6 +81,15 @@ public Action check(HttpServletRequest request,
8281
*/
8382
try {
8483

84+
byte[] bytes = null;
85+
86+
try {
87+
bytes = response.getInterceptingServletOutputStream().getResponseBytes();
88+
} catch (IOException ioe) {
89+
log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
90+
return new DoNothingAction(); // yes this is a fail open!
91+
}
92+
8593
String s = new String(bytes,charEnc);
8694

8795
if ( pattern.matcher(s).matches() ) {

src/main/java/org/owasp/esapi/waf/rules/ReplaceContentRule.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ public Action check(HttpServletRequest request,
5151
InterceptingHTTPServletResponse response,
5252
HttpServletResponse httpResponse) {
5353

54-
byte[] bytes = response.getInterceptingServletOutputStream().getResponseBytes();
55-
5654
/*
5755
* First early fail: if the URL doesn't match the paths we're interested in.
5856
*/
@@ -70,6 +68,16 @@ public Action check(HttpServletRequest request,
7068
return new DoNothingAction();
7169
}
7270
}
71+
72+
byte[] bytes = null;
73+
74+
try {
75+
bytes = response.getInterceptingServletOutputStream().getResponseBytes();
76+
} catch (IOException ioe) {
77+
log(request,"Error matching pattern '" + pattern.pattern() + "', IOException encountered (possibly too large?): " + ioe.getMessage() + " (in response to URL: '" + request.getRequestURL() + "')");
78+
return new DoNothingAction(); // yes this is a fail open!
79+
}
80+
7381

7482
try {
7583

src/test/java/org/owasp/esapi/waf/DynamicInsertionTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ public void testShouldReplaceContent() throws Exception {
4545
WAFTestUtility.createAndExecuteWAFTransaction( "waf-policies/dynamic-insertion-policy.xml", request, response );
4646

4747
assertTrue ( response.getStatus() == HttpServletResponse.SC_OK );
48-
assertTrue ( response.getBody().indexOf("test") > -1 );
49-
System.out.println( response.getBody() );
48+
String body = response.getBody();
49+
assertTrue ( body.indexOf("test") > -1 );
50+
System.out.println( body );
5051

5152
}
5253

0 commit comments

Comments
 (0)