2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/filter/ext_filter.h"
20 #include "hphp/runtime/ext/filter/logical_filters.h"
21 #include "hphp/runtime/ext/filter/sanitizing_filters.h"
23 #include "hphp/runtime/base/array-init.h"
24 #include "hphp/runtime/base/comparisons.h"
25 #include "hphp/runtime/base/init-fini-node.h"
26 #include "hphp/runtime/base/request-local.h"
27 #include "hphp/runtime/base/php-globals.h"
31 ///////////////////////////////////////////////////////////////////////////////
33 const int64_t k_INPUT_POST
= 0;
34 const int64_t k_INPUT_GET
= 1;
35 const int64_t k_INPUT_COOKIE
= 2;
36 const int64_t k_INPUT_ENV
= 4;
37 const int64_t k_INPUT_SERVER
= 5;
38 const int64_t k_INPUT_SESSION
= 6;
39 const int64_t k_INPUT_REQUEST
= 99;
40 const int64_t k_FILTER_FLAG_NONE
= 0;
41 const int64_t k_FILTER_REQUIRE_SCALAR
= 33554432;
42 const int64_t k_FILTER_REQUIRE_ARRAY
= 16777216;
43 const int64_t k_FILTER_FORCE_ARRAY
= 67108864;
44 const int64_t k_FILTER_NULL_ON_FAILURE
= 134217728;
45 const int64_t k_FILTER_VALIDATE_INT
= 257;
46 const int64_t k_FILTER_VALIDATE_BOOLEAN
= 258;
47 const int64_t k_FILTER_VALIDATE_FLOAT
= 259;
48 const int64_t k_FILTER_VALIDATE_REGEXP
= 272;
49 const int64_t k_FILTER_VALIDATE_URL
= 273;
50 const int64_t k_FILTER_VALIDATE_EMAIL
= 274;
51 const int64_t k_FILTER_VALIDATE_IP
= 275;
52 const int64_t k_FILTER_VALIDATE_MAC
= 276;
53 const int64_t k_FILTER_DEFAULT
= 516;
54 const int64_t k_FILTER_UNSAFE_RAW
= 516;
55 const int64_t k_FILTER_SANITIZE_STRING
= 513;
56 const int64_t k_FILTER_SANITIZE_STRIPPED
= 513;
57 const int64_t k_FILTER_SANITIZE_ENCODED
= 514;
58 const int64_t k_FILTER_SANITIZE_SPECIAL_CHARS
= 515;
59 const int64_t k_FILTER_SANITIZE_FULL_SPECIAL_CHARS
= 515;
60 const int64_t k_FILTER_SANITIZE_EMAIL
= 517;
61 const int64_t k_FILTER_SANITIZE_URL
= 518;
62 const int64_t k_FILTER_SANITIZE_NUMBER_INT
= 519;
63 const int64_t k_FILTER_SANITIZE_NUMBER_FLOAT
= 520;
64 const int64_t k_FILTER_SANITIZE_MAGIC_QUOTES
= 521;
65 const int64_t k_FILTER_CALLBACK
= 1024;
66 const int64_t k_FILTER_FLAG_ALLOW_OCTAL
= 1;
67 const int64_t k_FILTER_FLAG_ALLOW_HEX
= 2;
68 const int64_t k_FILTER_FLAG_STRIP_LOW
= 4;
69 const int64_t k_FILTER_FLAG_STRIP_HIGH
= 8;
70 const int64_t k_FILTER_FLAG_ENCODE_LOW
= 16;
71 const int64_t k_FILTER_FLAG_ENCODE_HIGH
= 32;
72 const int64_t k_FILTER_FLAG_ENCODE_AMP
= 64;
73 const int64_t k_FILTER_FLAG_NO_ENCODE_QUOTES
= 128;
74 const int64_t k_FILTER_FLAG_EMPTY_STRING_NULL
= 256;
75 const int64_t k_FILTER_FLAG_STRIP_BACKTICK
= 512;
76 const int64_t k_FILTER_FLAG_ALLOW_FRACTION
= 4096;
77 const int64_t k_FILTER_FLAG_ALLOW_THOUSAND
= 8192;
78 const int64_t k_FILTER_FLAG_ALLOW_SCIENTIFIC
= 16384;
79 const int64_t k_FILTER_FLAG_SCHEME_REQUIRED
= 65536;
80 const int64_t k_FILTER_FLAG_HOST_REQUIRED
= 131072;
81 const int64_t k_FILTER_FLAG_PATH_REQUIRED
= 262144;
82 const int64_t k_FILTER_FLAG_QUERY_REQUIRED
= 524288;
83 const int64_t k_FILTER_FLAG_IPV4
= 1048576;
84 const int64_t k_FILTER_FLAG_IPV6
= 2097152;
85 const int64_t k_FILTER_FLAG_NO_RES_RANGE
= 4194304;
86 const int64_t k_FILTER_FLAG_NO_PRIV_RANGE
= 8388608;
95 struct FilterRequestData final
{
97 // This doesn't copy them yet, but will do COW if they are modified
98 m_GET
= php_global(s_GET
).toArray();
99 m_POST
= php_global(s_POST
).toArray();
100 m_COOKIE
= php_global(s_COOKIE
).toArray();
101 m_SERVER
= php_global(s_SERVER
).toArray();
102 m_ENV
= php_global(s_ENV
).toArray();
105 void requestShutdown() {
113 Array
getVar(int64_t type
) {
115 case k_INPUT_GET
: return m_GET
;
116 case k_INPUT_POST
: return m_POST
;
117 case k_INPUT_COOKIE
: return m_COOKIE
;
118 case k_INPUT_SERVER
: return m_SERVER
;
119 case k_INPUT_ENV
: return m_ENV
;
121 return empty_array();
131 THREAD_LOCAL_NO_CHECK(FilterRequestData
, s_filter_request_data
);
133 static struct FilterExtension final
: Extension
{
134 FilterExtension() : Extension("filter", "0.11.0") {}
136 void moduleLoad(const IniSetting::Map
& /*ini*/, Hdf
/*config*/) override
{
137 HHVM_FE(__SystemLib_filter_input_get_var
);
138 HHVM_FE(_filter_snapshot_globals
);
141 void moduleInit() override
{
142 HHVM_RC_INT(INPUT_POST
, k_INPUT_POST
);
143 HHVM_RC_INT(INPUT_GET
, k_INPUT_GET
);
144 HHVM_RC_INT(INPUT_COOKIE
, k_INPUT_COOKIE
);
145 HHVM_RC_INT(INPUT_ENV
, k_INPUT_ENV
);
146 HHVM_RC_INT(INPUT_SERVER
, k_INPUT_SERVER
);
147 HHVM_RC_INT(INPUT_SESSION
, k_INPUT_SESSION
);
148 HHVM_RC_INT(INPUT_REQUEST
, k_INPUT_REQUEST
);
149 HHVM_RC_INT(FILTER_FLAG_NONE
, k_FILTER_FLAG_NONE
);
150 HHVM_RC_INT(FILTER_REQUIRE_SCALAR
, k_FILTER_REQUIRE_SCALAR
);
151 HHVM_RC_INT(FILTER_REQUIRE_ARRAY
, k_FILTER_REQUIRE_ARRAY
);
152 HHVM_RC_INT(FILTER_FORCE_ARRAY
, k_FILTER_FORCE_ARRAY
);
153 HHVM_RC_INT(FILTER_NULL_ON_FAILURE
, k_FILTER_NULL_ON_FAILURE
);
154 HHVM_RC_INT(FILTER_VALIDATE_INT
, k_FILTER_VALIDATE_INT
);
155 HHVM_RC_INT(FILTER_VALIDATE_BOOLEAN
, k_FILTER_VALIDATE_BOOLEAN
);
156 HHVM_RC_INT(FILTER_VALIDATE_FLOAT
, k_FILTER_VALIDATE_FLOAT
);
157 HHVM_RC_INT(FILTER_VALIDATE_REGEXP
, k_FILTER_VALIDATE_REGEXP
);
158 HHVM_RC_INT(FILTER_VALIDATE_URL
, k_FILTER_VALIDATE_URL
);
159 HHVM_RC_INT(FILTER_VALIDATE_EMAIL
, k_FILTER_VALIDATE_EMAIL
);
160 HHVM_RC_INT(FILTER_VALIDATE_IP
, k_FILTER_VALIDATE_IP
);
161 HHVM_RC_INT(FILTER_VALIDATE_MAC
, k_FILTER_VALIDATE_MAC
);
162 HHVM_RC_INT(FILTER_DEFAULT
, k_FILTER_DEFAULT
);
163 HHVM_RC_INT(FILTER_UNSAFE_RAW
, k_FILTER_UNSAFE_RAW
);
164 HHVM_RC_INT(FILTER_SANITIZE_STRING
, k_FILTER_SANITIZE_STRING
);
165 HHVM_RC_INT(FILTER_SANITIZE_STRIPPED
, k_FILTER_SANITIZE_STRIPPED
);
166 HHVM_RC_INT(FILTER_SANITIZE_ENCODED
, k_FILTER_SANITIZE_ENCODED
);
167 HHVM_RC_INT(FILTER_SANITIZE_SPECIAL_CHARS
, k_FILTER_SANITIZE_SPECIAL_CHARS
);
168 HHVM_RC_INT(FILTER_SANITIZE_FULL_SPECIAL_CHARS
,
169 k_FILTER_SANITIZE_FULL_SPECIAL_CHARS
);
170 HHVM_RC_INT(FILTER_SANITIZE_EMAIL
, k_FILTER_SANITIZE_EMAIL
);
171 HHVM_RC_INT(FILTER_SANITIZE_URL
, k_FILTER_SANITIZE_URL
);
172 HHVM_RC_INT(FILTER_SANITIZE_NUMBER_INT
, k_FILTER_SANITIZE_NUMBER_INT
);
173 HHVM_RC_INT(FILTER_SANITIZE_NUMBER_FLOAT
, k_FILTER_SANITIZE_NUMBER_FLOAT
);
174 HHVM_RC_INT(FILTER_SANITIZE_MAGIC_QUOTES
, k_FILTER_SANITIZE_MAGIC_QUOTES
);
175 HHVM_RC_INT(FILTER_CALLBACK
, k_FILTER_CALLBACK
);
176 HHVM_RC_INT(FILTER_FLAG_ALLOW_OCTAL
, k_FILTER_FLAG_ALLOW_OCTAL
);
177 HHVM_RC_INT(FILTER_FLAG_ALLOW_HEX
, k_FILTER_FLAG_ALLOW_HEX
);
178 HHVM_RC_INT(FILTER_FLAG_STRIP_LOW
, k_FILTER_FLAG_STRIP_LOW
);
179 HHVM_RC_INT(FILTER_FLAG_STRIP_HIGH
, k_FILTER_FLAG_STRIP_HIGH
);
180 HHVM_RC_INT(FILTER_FLAG_ENCODE_LOW
, k_FILTER_FLAG_ENCODE_LOW
);
181 HHVM_RC_INT(FILTER_FLAG_ENCODE_HIGH
, k_FILTER_FLAG_ENCODE_HIGH
);
182 HHVM_RC_INT(FILTER_FLAG_ENCODE_AMP
, k_FILTER_FLAG_ENCODE_AMP
);
183 HHVM_RC_INT(FILTER_FLAG_NO_ENCODE_QUOTES
, k_FILTER_FLAG_NO_ENCODE_QUOTES
);
184 HHVM_RC_INT(FILTER_FLAG_EMPTY_STRING_NULL
, k_FILTER_FLAG_EMPTY_STRING_NULL
);
185 HHVM_RC_INT(FILTER_FLAG_STRIP_BACKTICK
, k_FILTER_FLAG_STRIP_BACKTICK
);
186 HHVM_RC_INT(FILTER_FLAG_ALLOW_FRACTION
, k_FILTER_FLAG_ALLOW_FRACTION
);
187 HHVM_RC_INT(FILTER_FLAG_ALLOW_THOUSAND
, k_FILTER_FLAG_ALLOW_THOUSAND
);
188 HHVM_RC_INT(FILTER_FLAG_ALLOW_SCIENTIFIC
, k_FILTER_FLAG_ALLOW_SCIENTIFIC
);
189 HHVM_RC_INT(FILTER_FLAG_SCHEME_REQUIRED
, k_FILTER_FLAG_SCHEME_REQUIRED
);
190 HHVM_RC_INT(FILTER_FLAG_HOST_REQUIRED
, k_FILTER_FLAG_HOST_REQUIRED
);
191 HHVM_RC_INT(FILTER_FLAG_PATH_REQUIRED
, k_FILTER_FLAG_PATH_REQUIRED
);
192 HHVM_RC_INT(FILTER_FLAG_QUERY_REQUIRED
, k_FILTER_FLAG_QUERY_REQUIRED
);
193 HHVM_RC_INT(FILTER_FLAG_IPV4
, k_FILTER_FLAG_IPV4
);
194 HHVM_RC_INT(FILTER_FLAG_IPV6
, k_FILTER_FLAG_IPV6
);
195 HHVM_RC_INT(FILTER_FLAG_NO_RES_RANGE
, k_FILTER_FLAG_NO_RES_RANGE
);
196 HHVM_RC_INT(FILTER_FLAG_NO_PRIV_RANGE
, k_FILTER_FLAG_NO_PRIV_RANGE
);
198 HHVM_FE(filter_list
);
205 void threadInit() override
{
206 s_filter_request_data
.getCheck();
209 void requestShutdown() override
{
210 // warm up the s_filter_request_data
211 s_filter_request_data
->requestShutdown();
213 } s_filter_extension
;
216 InitFiniNode
globalsInit([]() { s_filter_request_data
->requestInit(); },
217 InitFiniNode::When::GlobalsInit
);
220 ///////////////////////////////////////////////////////////////////////////////
222 typedef struct filter_list_entry
{
225 Variant (*function
)(PHP_INPUT_FILTER_PARAM_DECL
);
228 static const filter_list_entry filter_list
[] = {
231 k_FILTER_VALIDATE_INT
,
234 StaticString("boolean"),
235 k_FILTER_VALIDATE_BOOLEAN
,
238 StaticString("float"),
239 k_FILTER_VALIDATE_FLOAT
,
242 StaticString("validate_regexp"),
243 k_FILTER_VALIDATE_REGEXP
,
244 php_filter_validate_regexp
246 StaticString("validate_url"),
247 k_FILTER_VALIDATE_URL
,
248 php_filter_validate_url
250 StaticString("validate_email"),
251 k_FILTER_VALIDATE_EMAIL
,
252 php_filter_validate_email
254 StaticString("validate_ip"),
255 k_FILTER_VALIDATE_IP
,
256 php_filter_validate_ip
258 StaticString("validate_mac"),
259 k_FILTER_VALIDATE_MAC
,
260 php_filter_validate_mac
262 StaticString("string"),
263 k_FILTER_SANITIZE_STRING
,
266 StaticString("stripped"),
267 k_FILTER_SANITIZE_STRING
,
270 StaticString("encoded"),
271 k_FILTER_SANITIZE_ENCODED
,
274 StaticString("special_chars"),
275 k_FILTER_SANITIZE_SPECIAL_CHARS
,
276 php_filter_special_chars
278 StaticString("full_special_chars"),
279 k_FILTER_SANITIZE_FULL_SPECIAL_CHARS
,
280 php_filter_full_special_chars
282 StaticString("unsafe_raw"),
284 php_filter_unsafe_raw
286 StaticString("email"),
287 k_FILTER_SANITIZE_EMAIL
,
291 k_FILTER_SANITIZE_URL
,
294 StaticString("number_int"),
295 k_FILTER_SANITIZE_NUMBER_INT
,
296 php_filter_number_int
298 StaticString("number_float"),
299 k_FILTER_SANITIZE_NUMBER_FLOAT
,
300 php_filter_number_float
302 StaticString("magic_quotes"),
303 k_FILTER_SANITIZE_MAGIC_QUOTES
,
304 php_filter_magic_quotes
306 StaticString("callback"),
314 s_default("default"),
315 s_options("options");
317 static Variant
fail(bool return_null
, const Variant
& options
) {
318 if (options
.isArray()) {
319 const Array
& arr(options
.toArray());
320 if (arr
.exists(s_default
)) {
321 return arr
[s_default
];
330 static const filter_list_entry
* php_find_filter(uint64_t id
) {
331 int i
, size
= sizeof(filter_list
) / sizeof(filter_list_entry
);
333 for (i
= 0; i
< size
; ++i
) {
334 if (filter_list
[i
].id
== id
) {
335 return &filter_list
[i
];
342 #define FAIL_IF(x) do { if (x) return false; } while (0)
344 static bool filter_var(Variant
& ret
, const Variant
& variable
, int64_t filter
,
345 const Variant
& options
) {
346 const filter_list_entry
* filter_func
= php_find_filter(filter
);
352 Variant option_array
;
353 if (options
.isArray()) {
354 auto arr
= options
.toArray();
355 flags
= arr
[s_flags
].toInt64();
356 option_array
= arr
[s_options
];
358 flags
= options
.toInt64();
361 FAIL_IF(variable
.isObject() && !variable
.getObjectData()->hasToString());
363 ret
= filter_func
->function(variable
.toString(), flags
, option_array
);
364 if (option_array
.isArray() && option_array
.toArray().exists(s_default
) &&
365 ((flags
& k_FILTER_NULL_ON_FAILURE
&& ret
.isNull()) ||
366 (!(flags
& k_FILTER_NULL_ON_FAILURE
) && ret
.isBoolean() &&
367 ret
.asBooleanVal() == 0))) {
368 ret
= option_array
.toArray()[s_default
];
373 static bool filter_recursive(Variant
& ret
, const Variant
& variable
, int64_t filter
,
374 const Variant
& options
) {
375 Array arr
= Array::Create();
376 for (ArrayIter
iter(variable
.toArray()); iter
; ++iter
) {
378 if (iter
.second().isArray()) {
379 FAIL_IF(!filter_recursive(v
, iter
.second().toArray(), filter
, options
));
381 FAIL_IF(!filter_var(v
, iter
.second(), filter
, options
));
383 arr
.add(iter
.first(), v
);
391 ///////////////////////////////////////////////////////////////////////////////
393 Variant
HHVM_FUNCTION(filter_list
) {
394 size_t size
= sizeof(filter_list
) / sizeof(filter_list_entry
);
396 for (size_t i
= 0; i
< size
; ++i
) {
397 ret
.append(filter_list
[i
].name
);
402 Variant
HHVM_FUNCTION(filter_id
,
403 const String
& filtername
) {
404 size_t size
= sizeof(filter_list
) / sizeof(filter_list_entry
);
405 for (size_t i
= 0; i
< size
; ++i
) {
406 if (filter_list
[i
].name
== filtername
) {
407 return filter_list
[i
].id
;
416 if (x) return fail(filter_flags & k_FILTER_NULL_ON_FAILURE, options); \
419 Variant
HHVM_FUNCTION(filter_var
,
420 const Variant
& variable
,
421 int64_t filter
/* = 516 */,
422 const Variant
& options
/* = empty_array_ref */) {
423 int64_t filter_flags
;
424 if (options
.isArray()) {
425 filter_flags
= options
.toCArrRef()[s_flags
].toInt64();
427 filter_flags
= options
.toInt64();
430 if (!(filter_flags
& k_FILTER_REQUIRE_ARRAY
||
431 filter_flags
& k_FILTER_FORCE_ARRAY
)) {
432 filter_flags
|= k_FILTER_REQUIRE_SCALAR
;
435 // No idea why, but zend does this..
436 if (filter
== k_FILTER_CALLBACK
) {
440 if (variable
.isArray()) {
441 FAIL_IF(filter_flags
& k_FILTER_REQUIRE_SCALAR
);
443 FAIL_IF(!filter_recursive(ret
, variable
, filter
, options
));
446 FAIL_IF(filter_flags
& k_FILTER_REQUIRE_ARRAY
);
449 FAIL_IF(!filter_var(ret
, variable
, filter
, options
));
450 if (filter_flags
& k_FILTER_FORCE_ARRAY
&& !ret
.isArray()) {
451 ret
= make_packed_array(ret
);
458 Array
HHVM_FUNCTION(__SystemLib_filter_input_get_var
, int64_t variable_name
) {
459 return s_filter_request_data
->getVar(variable_name
);
462 void HHVM_FUNCTION(_filter_snapshot_globals
) {
463 s_filter_request_data
->requestInit();
466 ///////////////////////////////////////////////////////////////////////////////