Skip to content

Commit 34021cf

Browse files
Tobias Simolikdacappo
Tobias Simolik
authored andcommitted
[Buffer] Taint API harmonization (nodejs#56)
* [Refactoring] change taint to _taint on buffer * [Refactor] Added _taint in unit tests * [Feature] Added method for buffer * [Test] Buffer.new() test for each encoding * [Test] Buffer.from() tests * [Test] Buffer.new() tests * [Test] Buffer.new() tests for one string + each encoding * [Test] Buffer.new() tests for concatenated ascii and utf8 encoding * [Test] Buffer.new() test enhanced for utf8 encoding * [Test] Buffer.new() tests for hex (ascii) encoding * [Test] Buffer.new() removed bug in unit tests * [Test] Buffer.new() tests for hex encoding with utf8 character * [Test] Buffer.new() unit tests for base64 encoding * [Test] Buffer.new() unit tests for base64 unicode encoding * [Test] Buffer.new() refactoring for utf8 + ascii encoding * [Buffer] Reactor taint propagation toString and slice * [Buffer] Remove previously inserted newlines * [Buffer] Taint propagation for Buffer.prototype.toString('hex') * [Test] Buffer.new() unit tests for utf8 encoding * [Feature] Buffer.alloc() keep taint * [Feature] Buffer.alloc() keep (sub-)taint for hex encoding * [Test] Buffer.fill() unit tests * [Feature] Buffer.write() ascii/uft8 taint propagation * [Test] Buffer.concat() unit tests * Buffer.write() refactored * Â[Feature] apply Taint to Partial Buffer * [Feature] Keep taint outsite taint ranges for write/fill * [Fix] Buffer.concat() taint propagation * [Merge] buffer refactoring * [Refactoring] Final buffer status * [Feature] Final status of buffer * [Feature] Final refactored buffer api status
1 parent ecc6aa3 commit 34021cf

26 files changed

+3353
-90
lines changed

lib/_http_common.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ function parserOnBody(b, start, len) {
122122
var slice = b.slice(start, start + len);
123123
// TaintNode
124124
// TODO: enable again
125-
//slice.taint = [{ 'begin': 0, 'end': slice.length }];
125+
//slice._taint = [{ 'begin': 0, 'end': slice.length }];
126126
var ret = stream.push(slice);
127127
if (!ret)
128128
readStop(this.socket);

lib/_pathTraversalCheck.js

+19-19
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ const Buffer = require('buffer').Buffer;
1515
function removePathTraversal(path) {
1616
if (!path)
1717
return path;
18-
if (path.isTainted && path.isTainted()) {
18+
if (typeof path === 'string' && path.isTainted()) {
1919
path = removeAllTaintedString(path, '..');
20-
} else if (path.taint && path.taint.length > 0) {
20+
} else if (Buffer.isBuffer(path) && path.isTainted()) {
2121
path = removeAllTaintedBuffer(path, '..');
2222
}
2323
return path;
@@ -68,12 +68,12 @@ function removeAllTaintedBuffer(path, toRemove) {
6868
var arr = [];
6969
var i;
7070
var endOfTaintTraversal = false;
71-
for (i = 0; i < path.taint.length; i++) {
72-
var currentTaintLength = path.taint.length;
71+
for (i = 0; i < path._taint.length; i++) {
72+
var currentTaintLength = path._taint.length;
7373
arr.push(addBeginning(path, i, endOfTaintTraversal));
7474
endOfTaintTraversal = checkEndOfTaintTraversal(path, i);
7575
arr.push(mitigateOneTaintBuffer(path, i, toRemove));
76-
if (path.taint.length < currentTaintLength)
76+
if (path._taint.length < currentTaintLength)
7777
i--;
7878
}
7979
arr.push(addEndOfPath(path, endOfTaintTraversal));
@@ -82,37 +82,37 @@ function removeAllTaintedBuffer(path, toRemove) {
8282
}
8383

8484
function checkEndOfTaintTraversal(path, i) {
85-
if (path.length > path.taint[i].end &&
86-
(path.slice(path.taint[i].end - 2,
87-
path.taint[i].end + 1).compare(Buffer.from('../')) === 0 ||
88-
path.slice(path.taint[i].end - 2,
89-
path.taint[i].end + 1).compare(Buffer.from('..\\')) === 0)) {
85+
if (path.length > path._taint[i].end &&
86+
(path.slice(path._taint[i].end - 2,
87+
path._taint[i].end + 1).compare(Buffer.from('../')) === 0 ||
88+
path.slice(path._taint[i].end - 2,
89+
path._taint[i].end + 1).compare(Buffer.from('..\\')) === 0)) {
9090
return true;
9191
}
9292
return false;
9393
}
9494

9595
function addBeginning(path, i, endOfTaintTraversal) {
9696
if (i === 0) {
97-
return path.slice(0, path.taint[i].begin);
98-
} else if (endOfTaintTraversal && path.taint[i].begin >
99-
path.taint[i - 1].end + 1) {
100-
return path.slice(path.taint[i - 1].end + 1, path.taint[i].begin);
97+
return path.slice(0, path._taint[i].begin);
98+
} else if (endOfTaintTraversal && path._taint[i].begin >
99+
path._taint[i - 1].end + 1) {
100+
return path.slice(path._taint[i - 1].end + 1, path._taint[i].begin);
101101
} else if (!endOfTaintTraversal) {
102-
return path.slice(path.taint[i - 1].end, path.taint[i].begin);
102+
return path.slice(path._taint[i - 1].end, path._taint[i].begin);
103103
}
104104
}
105105

106106
function addEndOfPath(path, endOfTaintTraversal) {
107107
if (endOfTaintTraversal)
108-
return path.slice(path.taint[path.taint.length - 1].end + 1);
108+
return path.slice(path._taint[path._taint.length - 1].end + 1);
109109
else
110-
return path.slice(path.taint[path.taint.length - 1].end);
110+
return path.slice(path._taint[path._taint.length - 1].end);
111111
}
112112

113113
function mitigateOneTaintBuffer(path, i, toRemove) {
114114
var removedCharCounter = [];
115-
var tainted = path.slice(path.taint[i].begin, path.taint[i].end);
115+
var tainted = path.slice(path._taint[i].begin, path._taint[i].end);
116116
var toRemoveList = [];
117117
var fixedBuffer = [];
118118

@@ -132,7 +132,7 @@ function mitigateOneTaintBuffer(path, i, toRemove) {
132132
function removeOneInstanceBuffer(path, tainted, fixedBuffer,
133133
toRemoveList, removedCharCounter,
134134
i, toRemove, counterTotal) {
135-
var endOfRemovedIndex = path.taint[i].begin + tainted.indexOf(toRemove) +
135+
var endOfRemovedIndex = path._taint[i].begin + tainted.indexOf(toRemove) +
136136
toRemove.length + counterTotal;
137137
if (endOfRemovedIndex < path.length &&
138138
(path.slice(endOfRemovedIndex, endOfRemovedIndex + 1)

lib/_taint_buffer.js

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
'use strict';
2+
3+
const { byteLength } = require('internal/buffer_util');
4+
5+
exports.utf8Slice = (buf, start, end) => {
6+
return slice(buf, start, end, 'utf8Slice');
7+
};
8+
9+
exports.asciiSlice = (buf, start, end) => {
10+
return slice(buf, start, end, 'asciiSlice');
11+
};
12+
13+
exports.ucs2Slice = (buf, start, end) => {
14+
return slice(buf, start, end, 'ucs2Slice');
15+
};
16+
17+
exports.latin1Slice = (buf, start, end) => {
18+
return slice(buf, start, end, 'latin1Slice');
19+
};
20+
21+
exports.hexSlice = (buf, start, end) => {
22+
return slice(buf, start, end, 'hexSlice');
23+
};
24+
25+
function slice(buf, start, end, encodingSlice) {
26+
let result = '';
27+
let i = start;
28+
for (const taint of getSubtaint(buf._taint, start, end)) {
29+
result += buf[encodingSlice](i, taint.begin);
30+
result += buf[encodingSlice](taint.begin, taint.end).setTaint('buffer');
31+
i = taint.end;
32+
}
33+
result += buf[encodingSlice](i, end);
34+
return result;
35+
}
36+
37+
function getSubtaint(taint, begin, end) {
38+
const result = [];
39+
for (var i in taint) {
40+
const range = taint[i];
41+
if (range.end < begin || range.begin > end) {
42+
continue;
43+
} else if (range.begin >= begin && range.end <= end) {
44+
result.push({ begin: range.begin, end: range.end });
45+
} else if (range.begin < begin && range.end <= end) {
46+
result.push({ begin: begin, end: range.end });
47+
} else if (range.begin < begin && range.end > end) {
48+
result.push({ begin: begin, end: end });
49+
} else if (range.begin >= begin && range.end > end) {
50+
result.push({ begin: range.begin, end: end });
51+
}
52+
}
53+
return result;
54+
}
55+
56+
exports.applyTaintToBuffer = applyTaintToBuffer;
57+
58+
function applyTaintToBuffer(buf, val, start, end, encoding) {
59+
60+
buf._taint = [];
61+
62+
if (typeof val !== 'string') return buf;
63+
64+
for (const taint of val.getTaint()) {
65+
const offset = byteLength(val.slice(0, taint.begin)
66+
, encoding);
67+
const offsetEnd = offset + byteLength(val.slice(taint.begin
68+
, taint.end), encoding);
69+
const helpTaint = [{ begin: offset, end: offsetEnd }];
70+
buf._taint.push(getSubtaint(helpTaint, start, end)[0]);
71+
}
72+
return buf;
73+
}
74+
75+
exports.ucs2Write = (buf, string, offset, length) => {
76+
return write(buf, string, offset, length, 'utf16le', 'ucs2Write');
77+
};
78+
79+
exports.utf8Write = (buf, string, offset, length) => {
80+
return write(buf, string, offset, length, 'utf8', 'utf8Write');
81+
};
82+
83+
exports.asciiWrite = (buf, string, offset, length) => {
84+
return write(buf, string, offset, length, 'ascii', 'asciiWrite');
85+
};
86+
87+
function write(buf, string, offset, length, encoding, encodingSlice) {
88+
const result = buf[encodingSlice](string, offset, length);
89+
applyTaintToPartialBuffer(buf, string, offset, length, encoding);
90+
return result;
91+
}
92+
93+
// exports.applyTaintToPartialBuffer = applyTaintToPartialBuffer;
94+
95+
function applyTaintToPartialBuffer(buf, string, offset, length, encoding) {
96+
97+
const end = offset + length;
98+
const taintResult = [];
99+
100+
// Step 1: Keep taint before string range to be inserted.
101+
for (const taint of buf._taint) {
102+
if (taint.end < offset) {
103+
taintResult.push(taint);
104+
} else if (taint.begin < offset && taint.end >= offset) {
105+
const helpTaint = [{ begin: taint.begin, end: offset - 1 }];
106+
taintResult.push(helpTaint);
107+
} else {
108+
break;
109+
}
110+
}
111+
// Step 2: Keep taint from string
112+
for (const taint of string.getTaint()) {
113+
const taintBegin = byteLength(string.slice(0, taint.begin)
114+
, encoding);
115+
const taintEnd = byteLength(string.slice(taint.begin, taint.end)
116+
, encoding);
117+
const helpTaint = [{ begin: offset + taintBegin,
118+
end: offset + taintEnd }];
119+
taintResult.push(getSubtaint(helpTaint, offset, end)[0]);
120+
}
121+
// Step 3: Keep taint after string range to be inserted.
122+
for (const taint of buf._taint) {
123+
if (taint.end <= end) {
124+
continue;
125+
} else if (taint.begin <= end && taint.end > end) {
126+
const helpTaint = [{ begin: end + 1, end: taint.end }];
127+
taintResult.push(helpTaint);
128+
} else if (taint.begin > end) {
129+
taintResult.push(taint);
130+
}
131+
}
132+
// Save the result
133+
buf._taint = taintResult;
134+
}
135+
136+
exports.concatBufferArrayTaint = concatBufferArrayTaint;
137+
138+
function concatBufferArrayTaint(list) {
139+
140+
const taintResult = [];
141+
var offset = 0;
142+
143+
for (const buffer of list) {
144+
if (buffer && buffer._taint) {
145+
for (const taint of buffer._taint) {
146+
taintResult.push({ begin: offset + taint.begin,
147+
end: offset + taint.end });
148+
}
149+
}
150+
offset += buffer.length;
151+
}
152+
return taintResult;
153+
}

lib/_taint_buffer_util.js

+20-21
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ const binding = process.binding('buffer');
88
exports.reapplyTaintToString = (string, buffer, start, end, encoding) => {
99
var i;
1010

11-
if (!buffer.taint || buffer.taint.length < 1) {
11+
if (!buffer._taint || buffer._taint.length < 1) {
1212
return string;
1313
}
14-
const taint = buffer.taint;
14+
const taint = buffer._taint;
1515
const resultString = '';
1616
// For hex, ascii, ucs2 I perform the reverse action of applyTaintToBuffer
1717
//TODO: das geht so nicht, ich schmeiß ja alles ohne taint weg!!!
@@ -108,8 +108,7 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
108108
writeOffset, length, written) => {
109109
// Some attention seeking comment here
110110
//if (!string.isTainted()) {
111-
buffer.taint.length;
112-
buffer.taint = [];
111+
buffer._taint = [];
113112
return buffer;
114113
//}
115114
const taint = []; //string.getTaint();
@@ -177,7 +176,7 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
177176
if (written > 0 && alreadyWritten > written)
178177
break;
179178
}
180-
buffer.taint = taint;
179+
buffer._taint = taint;
181180
}
182181
if (['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].indexOf(encoding) > -1) {
183182
if (written < 0) {
@@ -201,7 +200,7 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
201200
}
202201
}
203202
}
204-
buffer.taint = taint;
203+
buffer._taint = taint;
205204
}
206205
if (['hex'].indexOf(encoding) > -1) {
207206
if (written < 0) {
@@ -220,21 +219,21 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
220219
}
221220
}
222221
}
223-
buffer.taint = taint;
222+
buffer._taint = taint;
224223
}
225224
//TODO
226225
if (['base64'].indexOf(encoding) > -1) {
227226
// nothing written
228227
if (written === 0) {
229-
buffer.taint = [];
228+
buffer._taint = [];
230229
return buffer;
231230
}
232231
// whole original string is tainted with the same range
233232
if (taint.length === 1 && taint[0].begin === 0 &&
234233
taint[0].end === string.length) {
235234
taint[0].begin += writeOffset;
236235
taint[0].end = written + writeOffset;
237-
buffer.taint = taint;
236+
buffer._taint = taint;
238237
return buffer;
239238
}
240239

@@ -264,7 +263,7 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
264263
newTaint.push({ begin: newBegin, end: newEnd, flow: curr.flow });
265264
}
266265
}
267-
buffer.taint = newTaint;
266+
buffer._taint = newTaint;
268267
}
269268
if (['ascii', 'binary', 'raw', 'raws'].indexOf(encoding) > -1) {
270269
// one byte stays one byte
@@ -278,16 +277,16 @@ exports.applyTaintToBuffer = (buffer, string, encoding,
278277
}
279278
}
280279
}
281-
buffer.taint = taint;
280+
buffer._taint = taint;
282281
}
283282

284283
return buffer;
285284
};
286285

287286
exports.concatBufferArrayTaint = (list) => {
288287
return list.reduce((acc, val) => {
289-
if (typeof val === 'object' && val.taint) {
290-
val.taint.forEach((range) => {
288+
if (typeof val === 'object' && val._taint) {
289+
val._taint.forEach((range) => {
291290
acc.taint.push({ 'begin': range.begin + acc.len,
292291
'end': range.end + acc.len,
293292
'flow': range.flow
@@ -354,11 +353,11 @@ exports.writeBytesToBuffer = (offset, byteLength, string,
354353
}
355354
newEnd = (curr.begin - string.length) * -1;
356355
}
357-
buffer.taint.push({
356+
buffer._taint.push({
358357
begin: (newBegin / 2) + offset,
359358
end: (newEnd / 2) + offset, flow: curr.flow });
360359
} else {
361-
buffer.taint.push({
360+
buffer._taint.push({
362361
begin: (curr.begin / 2) + offset,
363362
end: (curr.end / 2) + offset, flow: curr.flow });
364363
}
@@ -372,8 +371,8 @@ exports.writeBytesToBuffer = (offset, byteLength, string,
372371
*/
373372
exports.subtaint = (buffer, begin, end) => {
374373
const newTaint = [];
375-
for (var i = 0; i < buffer.taint.length; i++) {
376-
const element = buffer.taint[i];
374+
for (var i = 0; i < buffer._taint.length; i++) {
375+
const element = buffer._taint[i];
377376
if (element.begin < end && element.end > begin) {
378377
newTaint.push({
379378
begin: Math.max(element.begin, begin) - begin,
@@ -389,8 +388,8 @@ exports.subtaint = (buffer, begin, end) => {
389388
exports.insert = (buffer, index, taints) => {
390389
const newTaint = [];
391390

392-
for (var i = 0; i < buffer.taint.length; i++) {
393-
const range = buffer.taint[i];
391+
for (var i = 0; i < buffer._taint.length; i++) {
392+
const range = buffer._taint[i];
394393
if (range.end <= index) {
395394
newTaint.push({ begin: range.begin, end: range.end, flow: range.flow });
396395
}
@@ -405,8 +404,8 @@ exports.insert = (buffer, index, taints) => {
405404
last = range.end + index;
406405
}
407406

408-
for (i = 0; i < buffer.taint.length; i++) {
409-
const range = buffer.taint[i];
407+
for (i = 0; i < buffer._taint.length; i++) {
408+
const range = buffer._taint[i];
410409
if (range.begin >= last) {
411410
newTaint.push({ begin: range.begin, end: range.end, flow: range.flow });
412411
}

0 commit comments

Comments
 (0)