How to encode System.IO.Stream.Write(byte[] buffer, int offset, int count) in VB.Net

Hackney, Jim 1 Reputation point
2022-12-09T15:33:34.707+00:00

I recently took over maintenance on an old legacy VB.Net web forms app. It has failed a Veracode scan with "Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)". I have fixed all of the problems except for one which I have been unable to figure out. I'm trying to encode System.IO.Stream.Write(byte[] buffer, int offset, int count) but nothing I've tried has worked and I'm stuck. I am hoping someone can provide some guidance. Here is the code:

 Dim serverResponse As System.Net.WebResponse = Nothing  
        Try  
            serverResponse = forwardToServer(context, requestUri, postBody, credentials)  
        Catch webExc As System.Net.WebException  
  
            Dim errorMsg As String = Convert.ToString(webExc.Message + " ") & uri  
            log(TraceLevel.[Error], errorMsg)  
  
            If webExc.Response IsNot Nothing Then  
                copyHeaders(TryCast(webExc.Response, System.Net.HttpWebResponse), context.Response)  
  
                Using responseStream As Stream = webExc.Response.GetResponseStream()  
                    Dim bytes As Byte() = New Byte(32767) {}  
                    Dim bytesRead As Integer = 0  
  
                    While (InlineAssignHelper(bytesRead, responseStream.Read(bytes, 0, bytes.Length))) > 0  
                        responseStream.Write(bytes, 0, bytesRead)  
                    End While  
  
                    context.Response.StatusCode = CInt(TryCast(webExc.Response, System.Net.HttpWebResponse).StatusCode)  
                    context.Response.OutputStream.Write(bytes, 0, bytes.Length)  
                End Using  
            Else  
                Dim statusCode As System.Net.HttpStatusCode = System.Net.HttpStatusCode.InternalServerError  
                sendErrorResponse(context.Response, Nothing, errorMsg, statusCode)  
            End If  
            Return  
        End Try  

The problem I am having is with this: responseStream.Write(bytes, 0, bytesRead) on line 17

Developer technologies | VB
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. LesHay 7,141 Reputation points
    2022-12-09T16:01:47.027+00:00

    Hi
    You talk of 'The problem I am having', but telling where is only half of required info - you need to also mention what the problem is too.

    From MS Docs, the only thing I can see is that the 3rd parameter in your call to .Write may be zero? See the site for possible Exceptions. When the code execution reached the problem line, what are the parameter values?


  2. Michael Taylor 60,341 Reputation points
    2022-12-09T17:34:32.017+00:00

    I'm curious how this code is actually working to be honest. It appears that in the case of an exception and a response body is sent back then you're going to read the error response and write out something else. The issue I see is that you're reading and writing to the response stream. The response stream is what came back from the original request and can't/shouldn't be modified.

       While (InlineAssignHelper(bytesRead, responseStream.Read(bytes, 0, bytes.Length))) > 0  
          responseStream.Write(bytes, 0, bytesRead)  
       End While  
    

    I'm surprised that would work as I assumed the response stream was read only. Even if it did allow writing, you are in a loop to read the response in 32K chunks (approximately) so you're mixing read and write code on the same stream. This isn't going to end well. A stream has only one "position" field. Reading and writing use that. So imagine that you read the first 32K of data from the stream. Then you write out that "encoded" data to the same stream. It'll be after the unencoded version (unless your helper method is doing far more than just reading).

    Personally I would recommend you remove this code from here. If you need to transform stream data then you create a new stream class that accepts another stream class as a parameter. Then the stream can transform the data as it is being read (if ever). This is how encryption and compression streams work and can be applied to any stream, anywhere. Imagine you create a custom EncodeStream class. Your existing code simplifies down to this.

       Using responseStream As Stream = new EncodedStream(webExc.Response.GetResponseStream())  
         
          context.Response.StatusCode = CInt(TryCast(webExc.Response, System.Net.HttpWebResponse).StatusCode)  
          responseStream.CopyTo(context.Response.OutputStream)  
       End Using  
    

    The class to wrap a stream isn't hard to write but does require knowledge of your "encoding" rules which you didn't really provide. It is also very important that you properly handle clean up of the base stream. Here's the start of a Stream wrapper class but I haven't tested it.

       Imports System.IO  
         
       Public Class EncodedStream  
           Inherits Stream  
         
           Public Sub New(stream As Stream)  
               InnerStream = stream  
           End Sub  
         
           Public Overrides ReadOnly Property CanRead As Boolean  
               Get  
                   Return InnerStream.CanRead  
               End Get  
           End Property  
         
           Public Overrides ReadOnly Property CanSeek As Boolean  
               Get  
                   Return InnerStream.CanSeek  
               End Get  
           End Property  
         
           Public Overrides ReadOnly Property CanWrite As Boolean  
               Get  
                   Return InnerStream.CanWrite  
               End Get  
           End Property  
         
           Public Overrides ReadOnly Property Length As Long  
               Get  
                   Return InnerStream.Length  
               End Get  
           End Property  
         
           Public Overrides Property Position As Long  
               Get  
                   Return InnerStream.Position  
               End Get  
         
               Set  
                   InnerStream.Position = Value  
               End Set  
           End Property  
         
           Protected Overrides Sub Dispose(disposing As Boolean)  
               If (disposing) Then  
                   InnerStream.Dispose()  
               End If  
           End Sub  
         
           Public Overrides Sub Flush()  
               InnerStream.Flush()  
           End Sub  
         
           Public Overrides Function Read(buffer As Byte(), offset As Integer, count As Integer) As Integer  
         
               Return InnerStream.Read(buffer, offset, count)  
           End Function  
         
           Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long  
         
               Return InnerStream.Seek(offset, origin)  
           End Function  
         
           Public Overrides Sub SetLength(value As Long)  
               InnerStream.SetLength(value)  
           End Sub  
         
           Public Overrides Sub Write(buffer As Byte(), offset As Integer, count As Integer)  
               InnerStream.Write(buffer, offset, count)  
           End Sub  
         
           Protected Property InnerStream As Stream  
         
       End Class  
    

    The important part here is the Read method. Override it to do your "encoding" based upon whatever rules you need. Looking at your original code I suspect you'll read from the inner stream, buffered, and call your encoding logic that you're currently using a while loop for. You'll need to add some logic to handle the case of the reader not reading an entire buffer at once and whatnot but it shouldn't be too difficult to figure out. You can refer to the source for one of the existing compression or encryption streams to get a better feel for how to do this.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.