-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathconfuser.js
171 lines (157 loc) · 5.14 KB
/
confuser.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
const { ProbeManager, DDPMessage, DDPClient } = require('./ddp');
const util = require('./util');
/**
* Generate an object describing the type structure of the parameter for use in
* type confusion attacks. Scalars just get a string, such as "string" or
* "number," but objects are parsed recursively. {"parameters":{"ids":[1,2,3]}}
* becomes {"parameters":{"ids":["number", "number", "number"]}}.
**/
function typeDescriptor(x) {
if (typeof x !== 'object') {
return typeof x;
}
if (Array.isArray(x)) {
return x.map((y) => typeDescriptor(y));
}
var keys = Object.keys(x).sort();
var obj = {};
keys.forEach((k) => {
obj[k] = typeDescriptor(x[k])
});
return obj;
}
function typesEqual(a, b) {
return JSON.stringify(typeDescriptor(a)) === JSON.stringify(typeDescriptor(b));
}
const scalarTypes = ['string', 'number', 'boolean'];
const allTypes = ['string', 'number', 'boolean', 'object'];
//cartesian product - stolen from https://stackoverflow.com/a/43053803
let f = (a, b) => [].concat(...a.map(a => b.map(b => [].concat(a, b))));
let cartesian = (a, b, ...c) => b ? cartesian(f(a, b), ...c) : a;
function maxCount(a) {
let count = 0;
a.forEach((entry) => {
count = (entry.length > count) ? entry.length : count;
});
return count;
}
/**
* Generate "confusers" for a type descriptor. A "confuser" describes a single
* type switch to a single element in a nested parameter. Each confuser is an
* object with an accessPath (which describes how to get to the targeted
* element) and a types array, listing the different types to swap in for the
* original value.
**/
function getConfusers(td) {
return getConfusersRec(td).filter((x) => x.accessPath.length > 0);
}
function getConfusersRec(td) {
if (typeof td === 'string') {
let newTypes = allTypes.filter((x) => x !== td);
return [{'accessPath':[], 'types':newTypes}];
}
if (Array.isArray(td)) {
let confusers = [];
let inner = td.map((x) => getConfusersRec(x));
for (var i = 0; i < inner.length; ++i) {
for (var j = 0; j < inner[i].length; ++j) {
confusers.push({
'accessPath':[i].concat(inner[i][j].accessPath),
'types': inner[i][j].types
});
}
}
confusers.push({
'accessPath':[],
'types':scalarTypes
});
return confusers;
}
var keys = Object.keys(td).sort();
let confusers = [];
keys.forEach((k) => {
let inner = getConfusersRec(td[k]);
inner.forEach((m) => {
confusers.push({
'accessPath':[k].concat(m.accessPath),
'types':m.types
});
});
});
confusers.push({
'accessPath':[],
'types':scalarTypes
});
return confusers;
}
/**
* A single confuser probe generated by ConfuserProbeManager will have an
* access path and a single type (instead of an array of types like in a
* typeDescriptor.
**/
const values = {
'object': {'key': 'value'},
'number': 5,
'string': 'helloworld',
'boolean': true
};
function makeValue(type) {
return values[type];
}
function applyConfuserProbeToParams(params, probe) {
let n = probe.accessPath.length;
let t = params;
for (var i = 0; i < n - 1; ++i) {
t = t[probe.accessPath[i]];
}
t[probe.accessPath[n - 1]] = makeValue(probe.type);
}
//turn confusers into a list of ProbeManager inputs
function inputsFromConfusers(confusers) {
//each confuser has one access path and a list of types
//turn it into a list of single-type probe inputs
//map each confuser into an array, then flatten
let inp = confusers.map((c) => (c.types.map((t) => ({
'accessPath': c.accessPath,
'type': t,
'name': c.accessPath.join('.') + "-" + t
})))).reduce((x,y) => x.concat(y));
return inp;
}
//take a message, generate its typeDescriptor, generate the typeDescriptor's
//confusers and generate the confuser's probe inputs.
function confuserInputs(message) {
if (!message.params || !message.params.length) {
return [];
}
let td = typeDescriptor(message.params);
let c = getConfusers(td);
return inputsFromConfusers(c);
}
/**
* ProbeManager that takes a single message in the options input, then
* automatically generates confusers and supplies a generateMessage callback.
* Note that only the constructor is overridden!
**/
class ConfuserProbeManager extends ProbeManager {
constructor(client, options) {
super(client, options);
let mgr = this;
let td = typeDescriptor(this.opt.message.params);
let confusers = getConfusers(td);
this.opt.inputs = inputsFromConfusers(confusers);
this.opt.baseMessage = util.ejsonClone(this.opt.message);
this.opt.generateMessage = (x) => {
let msg = util.ejsonClone(mgr.opt.baseMessage);
applyConfuserProbeToParams(msg.params, x);
return msg;
};
this.opt.generateAnswer = (x) => {
if (x.error && x.error.error === 400 && x.error.reason === 'Match failed') {
return undefined;
}
return x;
};
}
};
module.exports = {typeDescriptor, getConfusers, applyConfuserProbeToParams, typesEqual, ConfuserProbeManager};