View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2018 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.xml.pom;
19  
20  import java.io.BufferedInputStream;
21  import java.io.FilterInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  /**
26   * Filters everything in an input stream prior to the <project> element.
27   * This is useful to filter out the DOCTYPE declarations that can cause parsing
28   * issues.
29   *
30   * @author Jeremy Long
31   */
32  public class PomProjectInputStream extends FilterInputStream {
33  
34      /**
35       * The project tag for a pom.xml.
36       */
37      private static final byte[] PROJECT = {60, 112, 114, 111, 106, 101, 99, 116};
38      //private static final byte[] PROJECT = "<project".getBytes();
39  
40      /**
41       * The size of the buffer used to scan the input stream.
42       */
43      protected static final int BUFFER_SIZE = 1024;
44  
45      /**
46       * Constructs a new POM project filtering input stream. The input stream is
47       * wrapped in a buffered input stream.
48       *
49       * @param in the input stream
50       * @throws IOException thrown if there is an I/O error
51       */
52      public PomProjectInputStream(InputStream in) throws IOException {
53          super(new BufferedInputStream(in));
54          skipToProject();
55      }
56  
57      /**
58       * Skips bytes from the input stream until it finds the &lt;project&gt;
59       * element.
60       *
61       * @throws IOException thrown if an I/O error occurs
62       */
63      private void skipToProject() throws IOException {
64          final byte[] buffer = new byte[BUFFER_SIZE];
65          super.mark(BUFFER_SIZE);
66          int count = super.read(buffer, 0, BUFFER_SIZE);
67          while (count > 0) {
68              final int pos = findSequence(PROJECT, buffer);
69              if (pos >= 0) {
70                  super.reset();
71                  final long skipped = super.skip((long) pos);
72                  if (skipped != pos) {
73                      throw new IOException("Error skipping pom header information");
74                  }
75                  return;
76              } else if (count - PROJECT.length == 0) {
77                  return;
78              }
79              super.reset();
80              final long skipTo = (long) count - PROJECT.length;
81              final long skipped = super.skip(skipTo);
82              if (skipped != skipTo) {
83                  throw new IOException("Error skipping pom header information");
84              }
85              super.mark(BUFFER_SIZE);
86              count = super.read(buffer, 0, BUFFER_SIZE);
87          }
88      }
89  
90      /**
91       * Tests the buffer to see if it contains the given sequence[1]..[n]. It is
92       * assumed that sequence[0] is checked prior to calling this method and that
93       * buffer[pos] equals sequence[0].
94       *
95       * @param sequence the prefix to scan against
96       * @param buffer the buffer to scan
97       * @param pos the position in the buffer to being searching
98       * @return <code>true</code>if the next set of bytes from the input stream
99       * match the contents of the prefix.
100      */
101     private static boolean testRemaining(byte[] sequence, byte[] buffer, int pos) {
102         boolean match = true;
103         for (int i = 1; i < sequence.length; i++) {
104             if (buffer[pos + i] != sequence[i]) {
105                 match = false;
106                 break;
107             }
108         }
109         return match;
110     }
111 
112     /**
113      * Finds the start of the given sequence in the buffer. If not found, -1 is
114      * returned.
115      *
116      * @param sequence the sequence to locate
117      * @param buffer the buffer to search
118      * @return the starting position of the sequence in the buffer if found;
119      * otherwise -1
120      */
121     protected static int findSequence(byte[] sequence, byte[] buffer) {
122         int pos = -1;
123         for (int i = 0; i < buffer.length - sequence.length + 1; i++) {
124             if (buffer[i] == sequence[0] && testRemaining(sequence, buffer, i)) {
125                 pos = i;
126                 break;
127             }
128         }
129         return pos;
130     }
131 }