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 <project>
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 }