Skip to content

Commit 509b422

Browse files
committed
Implemented some of the filter_var filters
1 parent 265a0ed commit 509b422

File tree

2 files changed

+550
-30
lines changed

2 files changed

+550
-30
lines changed

_workbench/filter/filter_var.js

Lines changed: 270 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,279 @@
1-
function filter_var (v, filter, options) {
1+
function filter_var(input, filter, options) {
22
// http://kevin.vanzonneveld.net
33
// + original by: Brett Zamir (http://brett-zamir.me)
4+
// + improved by: Rafał Kukawski (http://kukawski.pl)
5+
// - depends on: addslashes
6+
// - depends on: htmlspecialchars
7+
// - depends on: strip_tags
48
// * example 1: filter_var('true', 'FILTER_VALIDATE_BOOLEAN');
59
// * returns 1: true
610

7-
var OPTS = {
8-
FILTER_VALIDATE_BOOLEAN: 1,
9-
10-
},
11-
FLAGS = {
12-
FILTER_NULL_ON_FAILURE: 1,
13-
},
14-
getOption = function (option, OPTIONS) {
15-
var optionNames = 0, fl = 0;
16-
if (typeof option === 'number') { // Allow for a single string or an array of string option
17-
optionNames = option;
11+
function is(val, type) {
12+
if (val == null) {
13+
return type === "null";
14+
}
15+
16+
if (type === "primitive") {
17+
return val !== Object(val);
18+
}
19+
20+
var actual = typeof val;
21+
22+
if (actual === "object") {
23+
return {
24+
"[object Array]": "array",
25+
"[object RegExp]": "regex"
26+
} [Object.prototype.toString.call(val)] || "object";
27+
}
28+
29+
if (actual === "number") {
30+
if (isNaN(val)) {
31+
return type === "nan";
32+
}
33+
34+
if (!isFinite(val)) {
35+
return "inf";
36+
}
37+
}
38+
39+
return type === actual;
40+
}
41+
42+
function str2regex(str) {}
43+
44+
function isPrimitive(val) {
45+
return val !== Object(val);
46+
}
47+
48+
var supportedFilters = {
49+
FILTER_VALIDATE_INT: 257,
50+
FILTER_VALIDATE_BOOLEAN: 258,
51+
FILTER_VALIDATE_FLOAT: 259,
52+
FILTER_VALIDATE_REGEXP: 272,
53+
FILTER_VALIDATE_URL: 273,
54+
FILTER_VALIDATE_EMAIL: 274,
55+
FILTER_VALIDATE_IP: 275,
56+
57+
FILTER_SANITIZE_STRING: 513,
58+
FILTER_SANITIZE_STRIPPED: 513,
59+
FILTER_SANITIZE_ENCODED: 514,
60+
FILTER_SANITIZE_SPECIAL_CHARS: 515,
61+
FILTER_UNSAFE_RAW: 516,
62+
FILTER_DEFAULT: 516,
63+
FILTER_SANITIZE_EMAIL: 517,
64+
FILTER_SANITIZE_URL: 518,
65+
FILTER_SANITIZE_NUMBER_INT: 519,
66+
FILTER_SANITIZE_NUMBER_FLOAT: 520,
67+
FILTER_SANITIZE_MAGIC_QUOTES: 521,
68+
// TODO: doesn't exist on my server. Add constant value
69+
FILTER_SANITIZE_FULL_SPECIAL_CHARS: -1,
70+
FILTER_CALLBACK: 1024
71+
};
72+
73+
var supportedFlags = {
74+
FILTER_FLAG_ALLOW_OCTAL: 1,
75+
FILTER_FLAG_ALLOW_HEX: 2,
76+
FILTER_FLAG_STRIP_LOW: 4,
77+
FILTER_FLAG_STRIP_HIGH: 8,
78+
FILTER_FLAG_ENCODE_LOW: 16,
79+
FILTER_FLAG_ENCODE_HIGH: 32,
80+
FILTER_FLAG_ENCODE_AMP: 64,
81+
FILTER_FLAG_NO_ENCODE_QUOTES: 128,
82+
FILTER_FLAG_ALLOW_FRACTION: 4096,
83+
FILTER_FLAG_ALLOW_THOUSAND: 8192,
84+
FILTER_FLAG_ALLOW_SCIENTIFIC: 16384,
85+
FILTER_FLAG_PATH_REQUIRED: 262144,
86+
FILTER_FLAG_QUERY_REQUIRED: 524288,
87+
FILTER_FLAG_IPV4: 1048576,
88+
FILTER_FLAG_IPV6: 2097152,
89+
FILTER_FLAG_NO_RES_RANGE: 4194304,
90+
FILTER_FLAG_NO_PRIV_RANGE: 8388608,
91+
FILTER_NULL_ON_FAILURE: 134217728
92+
};
93+
94+
if (is(filter, "null")) {
95+
filter = supportedFilters.FILTER_DEFAULT;
96+
} else if (is(filter, "string")) {
97+
filter = supportedFilters[filter];
98+
}
99+
100+
var flags = 0;
101+
if (is(options, "number")) {
102+
flags = options;
103+
} else if (is(options, "string")) {
104+
flags = supportedFlags[options] || 0;
105+
} else if (is(options, "object") && is(options.flags, "number")) {
106+
flags = options.flags;
107+
}
108+
109+
var opts = {};
110+
111+
if (is(options, "object")) {
112+
opts = options.options || {};
113+
}
114+
115+
// it looks like the FILTER_NULL_ON_FAILURE is used across all filters, not only FILTER_VALIDATE_BOOLEAN
116+
// thus the failure var
117+
var failure = (flags & supportedFlags.FILTER_NULL_ON_FAILURE) ? null: false;
118+
119+
if (!is(filter, "number")) {
120+
// no numeric filter, return
121+
return failure;
122+
}
123+
124+
// Shortcut for non-primitive values. All are failures
125+
if (!isPrimitive(input)) {
126+
return failure;
127+
}
128+
129+
// if input is string, trim whitespace TODO: make a dependency on trim()?
130+
var data = is(input, "string") ? input.replace(/(^\s+)|(\s+$)/g, '') : input;
131+
132+
switch (filter) {
133+
case supportedFilters.FILTER_VALIDATE_BOOLEAN:
134+
return /^(?:1|true|yes|on)$/i.test(data) || (/^(?:0|false|no|off)$/i.test(data) ? false: failure);
135+
136+
case supportedFilters.FILTER_VALIDATE_INT:
137+
var numValue = +data;
138+
139+
if (!/^(?:0|[+\-]?[1-9]\d*)$/.test(data)) {
140+
if ((flags & supportedFlags.FILTER_FLAG_ALLOW_HEX) && /^0x[\da-f]+$/i.test(data)) {
141+
numValue = parseInt(data, 16);
142+
} else if ((flags & supportedFlags.FILTER_FLAG_ALLOW_OCTAL) && /^0[0-7]+$/.test(data)) {
143+
numValue = parseInt(data, 8);
18144
} else {
19-
option = [].concat(option);
20-
for (i = 0, fl = option.length; i < fl; i++) {
21-
if (OPTIONS[option[i]]) {
22-
optionNames = optionNames | OPTIONS[option[i]];
23-
}
145+
return failure;
146+
}
147+
}
148+
149+
var minValue = is(opts.min_range, "number") ? opts.min_range: -Infinity;
150+
var maxValue = is(opts.max_range, "number") ? opts.max_range: Infinity;
151+
152+
if (!is(numValue, "number") || numValue % 1 || numValue < minValue || numValue > maxValue) {
153+
return failure;
154+
}
155+
156+
return numValue;
157+
158+
case supportedFilters.FILTER_VALIDATE_REGEXP:
159+
if (is(options.regexp, "regex")) {
160+
// FIXME: we are passing pre-processed input data (trimmed data).
161+
// check whether PHP also passess trimmed input
162+
var matches = options.regexp(data)
163+
return matches ? matches[0] : failure;
164+
}
165+
// TODO: support passing regexes as strings "#regex#is"
166+
case supportedFilters.FILTER_VALIDATE_IP:
167+
var ipv4 = /^(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)\.(25[0-5]|2[0-4]\d|[01]?\d?\d)$/
168+
var ipv4privrange = /^(?:0?10|172\.0?(?:1[6-9]|2\d|3[01])|192\.168)\./;
169+
var ipv4resrange = /^(?:0?0?0\.|127\.0?0?0\.0?0?0\.0?0?1|128\.0?0?0\.|169\.254\.|191\.255\.|192\.0?0?0\.0?0?2\.|25[0-5]\.|2[34]\d\.|22[4-9]\.)/;
170+
// IPv6 regex taken from here: http://forums.intermapper.com/viewtopic.php?t=452
171+
var ipv6 = /^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$/;
172+
173+
var mode = (supportedFlags.FILTER_FLAG_IPV4 | supportedFlags.FILTER_FLAG_IPV6);
174+
175+
if (flags !== 0) {
176+
mode &= flags;
177+
}
178+
179+
if (mode & supportedFlags.FILTER_FLAG_IPV4) {
180+
var ip = ipv4.test(input);
181+
182+
if (ip) {
183+
if ((flags & supportedFlags.FILTER_FLAG_NO_PRIV_RANGE) && privrange.test(data)) {
184+
return failure;
185+
}
186+
187+
if ((flags & supportedFlags.FILTER_FLAG_NO_RES_RANGE) && resrange.test(data)) {
188+
return failure;
24189
}
190+
191+
return input;
192+
}
193+
}
194+
195+
if (mode & supportedFlags.FILTER_FLAG_IPV6) {
196+
var ip = ipv6.test(input);
197+
198+
if (ip) {
199+
// TODO: check ipv6 ranges
200+
return input;
25201
}
26-
return optionNames;
27-
};
28-
filter = getOption(filter, OPTS);
29-
options = getOption(options, FLAGS);
30-
31-
if (filter & OPTS.option_VALIDATE_BOOLEAN) {
32-
return (/^(?:1|true|on|yes)$/).test(v) ||
33-
(
34-
((options & FLAGS.option_NULL_ON_FAILURE) &&
35-
!(/^(?:0|false|off|no)?$/).test(v)) ? null : false
36-
);
37-
}
38-
202+
}
203+
204+
return failure;
205+
206+
case supportedFilters.FILTER_CALLBACK:
207+
var fn = opts;
208+
209+
if (is(fn, "string")) {
210+
fn = this.window[fn];
211+
}
212+
213+
if (is(fn, "function")) {
214+
return fn(input);
215+
}
216+
217+
return failure;
218+
219+
case supportedFilters.FILTER_SANITIZE_NUMBER_INT:
220+
return ("" + input).replace(/[^\d+\-]/g, "");
221+
222+
case supportedFilters.FILTER_SANITIZE_NUMBER_FLOAT:
223+
return ('' + input).replace(/[^\deE.,+\-]/g, '').replace(/[eE.,]/g,
224+
function(m) {
225+
return {
226+
'.': (filter & supportedFilters.FILTER_FLAG_ALLOW_FRACTION) ? '.': '',
227+
',': (filter & supportedFilters.FILTER_FLAG_ALLOW_THOUSAND) ? ',': '',
228+
'e': (filter & supportedFilters.FILTER_FLAG_ALLOW_SCIENTIFIC) ? 'e': '',
229+
'E': (filter & supportedFilters.FILTER_FLAG_ALLOW_SCIENTIFIC) ? 'e': ''
230+
} [m];
231+
});
232+
233+
/*case supportedFilters.FILTER_SANITIZE_MAGIC_QUOTES:
234+
return this.addslashes(input); // ('' + input).replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0')*/
235+
236+
case supportedFilters.FILTER_SANITIZE_URL:
237+
return ("" + data).replace(/[^a-zA-Z\d$\-_.+!*'(),{}|\\\^~\[\]`<>#%";\/?:@&=]/g, '');
238+
239+
case supportedFilters.FILTER_SANITIZE_EMAIL:
240+
return ("" + data).replace(/[^a-zA-Z\d!#$%&'*+\-\/=?\^_`{|}~@.\[\]]/g, '');
241+
242+
case supportedFilters.FILTER_DEFAULT:
243+
// is alias of FILTER_UNSAFE_RAW
244+
// fall-through
245+
case supportedFilters.FILTER_UNSAFE_RAW:
246+
data = input + "";
247+
248+
if (flags & supportedFlags.FILTER_FLAG_ENCODE_AMP) {
249+
data = data.replace(/&/g, "&#38");
250+
}
251+
252+
if ((supportedFlags.FILTER_FLAG_ENCODE_LOW |
253+
supportedFlags.FILTER_FLAG_STRIP_LOW |
254+
supportedFlags.FILTER_FLAG_ENCODE_HIGH |
255+
supportedFlags.FILTER_FLAG_STRIP_HIGH) &
256+
flags) {
257+
258+
data = data.replace(/[\s\S]/g,
259+
function(c) {
260+
var charCode = c.charCodeAt(0);
261+
262+
if (charCode < 32) {
263+
return (flags & supportedFlags.FILTER_FLAG_STRIP_LOW) ? "":
264+
(flags & supportedFlags.FILTER_FLAG_ENCODE_LOW) ? "&#" + charCode: c;
265+
} else if (charCode > 127) {
266+
return (flags & supportedFlags.FILTER_FLAG_STRIP_HIGH) ? "": (flags & supportedFlags.FILTER_FLAG_ENCODE_HIGH) ? "&#" + charCode: c;
267+
}
268+
269+
return c;
270+
});
271+
}
272+
273+
return data;
274+
default:
275+
return false;
276+
}
277+
278+
return false;
39279
}

0 commit comments

Comments
 (0)