mirror of
				https://github.com/TeaOSLab/EdgeAdmin.git
				synced 2025-11-04 05:00:25 +08:00 
			
		
		
		
	管理系统增加XSS和SQL注入攻击防御
This commit is contained in:
		@@ -1,6 +1,6 @@
 | 
				
			|||||||
package ttlcache
 | 
					package ttlcache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "github.com/cespare/xxhash"
 | 
					import "github.com/cespare/xxhash/v2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func HashKey(key []byte) uint64 {
 | 
					func HashKey(key []byte) uint64 {
 | 
				
			||||||
	return xxhash.Sum64(key)
 | 
						return xxhash.Sum64(key)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								internal/waf/injectionutils/libinjection/COPYING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								internal/waf/injectionutils/libinjection/COPYING
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					Copyright (c) 2012-2016, Nick Galbreath
 | 
				
			||||||
 | 
					All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					modification, are permitted provided that the following conditions are
 | 
				
			||||||
 | 
					met:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. Neither the name of the copyright holder nor the names of its
 | 
				
			||||||
 | 
					contributors may be used to endorse or promote products derived from
 | 
				
			||||||
 | 
					this software without specific prior written permission.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
				
			||||||
 | 
					"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
				
			||||||
 | 
					A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
				
			||||||
 | 
					HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
				
			||||||
 | 
					SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
				
			||||||
 | 
					LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
				
			||||||
 | 
					DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
				
			||||||
 | 
					THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
				
			||||||
 | 
					OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					https://github.com/client9/libinjection
 | 
				
			||||||
 | 
					http://opensource.org/licenses/BSD-3-Clause
 | 
				
			||||||
							
								
								
									
										1
									
								
								internal/waf/injectionutils/libinjection/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								internal/waf/injectionutils/libinjection/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					copy from https://github.com/libinjection/libinjection
 | 
				
			||||||
							
								
								
									
										65
									
								
								internal/waf/injectionutils/libinjection/src/libinjection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								internal/waf/injectionutils/libinjection/src/libinjection.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Copyright 2012-2016 Nick Galbreath
 | 
				
			||||||
 | 
					 * nickg@client9.com
 | 
				
			||||||
 | 
					 * BSD License -- see COPYING.txt for details
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * https://libinjection.client9.com/
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef LIBINJECTION_H
 | 
				
			||||||
 | 
					#define LIBINJECTION_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					# define LIBINJECTION_BEGIN_DECLS    extern "C" {
 | 
				
			||||||
 | 
					# define LIBINJECTION_END_DECLS      }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# define LIBINJECTION_BEGIN_DECLS
 | 
				
			||||||
 | 
					# define LIBINJECTION_END_DECLS
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIBINJECTION_BEGIN_DECLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Pull in size_t
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Version info.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is moved into a function to allow SWIG and other auto-generated
 | 
				
			||||||
 | 
					 * binding to not be modified during minor release changes.  We change
 | 
				
			||||||
 | 
					 * change the version number in the c source file, and not regenerated
 | 
				
			||||||
 | 
					 * the binding
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See python's normalized version
 | 
				
			||||||
 | 
					 * http://www.python.org/dev/peps/pep-0386/#normalizedversion
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const char* libinjection_version(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Simple API for SQLi detection - returns a SQLi fingerprint or NULL
 | 
				
			||||||
 | 
					 * is benign input
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param[in] s  input string, may contain nulls, does not need to be null-terminated
 | 
				
			||||||
 | 
					 * \param[in] slen input string length
 | 
				
			||||||
 | 
					 * \param[out] fingerprint buffer of 8+ characters.  c-string,
 | 
				
			||||||
 | 
					 * \return 1 if SQLi, 0 if benign.  fingerprint will be set or set to empty string.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_sqli(const char* s, size_t slen, char fingerprint[]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** ALPHA version of xss detector.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * NOT DONE.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param[in] s  input string, may contain nulls, does not need to be null-terminated
 | 
				
			||||||
 | 
					 * \param[in] slen input string length
 | 
				
			||||||
 | 
					 * \return 1 if XSS found, 0 if benign
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_xss(const char* s, size_t slen, int strictMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LIBINJECTION_END_DECLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* LIBINJECTION_H */
 | 
				
			||||||
@@ -0,0 +1,868 @@
 | 
				
			|||||||
 | 
					#include "libinjection_html5.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef DEBUG
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#define TRACE() printf("%s:%d\n", __FUNCTION__, __LINE__)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define TRACE()
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHAR_EOF -1
 | 
				
			||||||
 | 
					#define CHAR_NULL 0
 | 
				
			||||||
 | 
					#define CHAR_BANG 33
 | 
				
			||||||
 | 
					#define CHAR_DOUBLE 34
 | 
				
			||||||
 | 
					#define CHAR_PERCENT 37
 | 
				
			||||||
 | 
					#define CHAR_SINGLE 39
 | 
				
			||||||
 | 
					#define CHAR_DASH 45
 | 
				
			||||||
 | 
					#define CHAR_SLASH 47
 | 
				
			||||||
 | 
					#define CHAR_LT 60
 | 
				
			||||||
 | 
					#define CHAR_EQUALS 61
 | 
				
			||||||
 | 
					#define CHAR_GT 62
 | 
				
			||||||
 | 
					#define CHAR_QUESTION 63
 | 
				
			||||||
 | 
					#define CHAR_RIGHTB 93
 | 
				
			||||||
 | 
					#define CHAR_TICK 96
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* prototypes */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_skip_white(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_is_white(char ch);
 | 
				
			||||||
 | 
					static int h5_state_eof(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_data(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_tag_open(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_tag_name(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_tag_name_close(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_end_tag_open(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_self_closing_start_tag(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_attribute_name(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_after_attribute_name(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_before_attribute_name(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_before_attribute_value(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_double_quote(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_single_quote(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_back_quote(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_no_quote(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_comment(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_cdata(h5_state_t* hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 12.2.4.44 */
 | 
				
			||||||
 | 
					static int h5_state_bogus_comment(h5_state_t* hs);
 | 
				
			||||||
 | 
					static int h5_state_bogus_comment2(h5_state_t* hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 12.2.4.45 */
 | 
				
			||||||
 | 
					static int h5_state_markup_declaration_open(h5_state_t* hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 8.2.4.52 */
 | 
				
			||||||
 | 
					static int h5_state_doctype(h5_state_t* hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * public function
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    memset(hs, 0, sizeof(h5_state_t));
 | 
				
			||||||
 | 
					    hs->s = s;
 | 
				
			||||||
 | 
					    hs->len = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (flags) {
 | 
				
			||||||
 | 
					    case DATA_STATE:
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case VALUE_NO_QUOTE:
 | 
				
			||||||
 | 
					        hs->state = h5_state_before_attribute_name;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case VALUE_SINGLE_QUOTE:
 | 
				
			||||||
 | 
					        hs->state = h5_state_attribute_value_single_quote;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case VALUE_DOUBLE_QUOTE:
 | 
				
			||||||
 | 
					        hs->state = h5_state_attribute_value_double_quote;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case VALUE_BACK_QUOTE:
 | 
				
			||||||
 | 
					        hs->state = h5_state_attribute_value_back_quote;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * public function
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_h5_next(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    assert(hs->state != NULL);
 | 
				
			||||||
 | 
					    return (*hs->state)(hs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Everything below here is private
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_is_white(char ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * \t = horizontal tab = 0x09
 | 
				
			||||||
 | 
					     * \n = newline = 0x0A
 | 
				
			||||||
 | 
					     * \v = vertical tab = 0x0B
 | 
				
			||||||
 | 
					     * \f = form feed = 0x0C
 | 
				
			||||||
 | 
					     * \r = cr  = 0x0D
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    return strchr(" \t\n\v\f\r", ch) != NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_skip_white(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					    while (hs->pos < hs->len) {
 | 
				
			||||||
 | 
					        ch = hs->s[hs->pos];
 | 
				
			||||||
 | 
					        switch (ch) {
 | 
				
			||||||
 | 
					        case 0x00: /* IE only */
 | 
				
			||||||
 | 
					        case 0x20:
 | 
				
			||||||
 | 
					        case 0x09:
 | 
				
			||||||
 | 
					        case 0x0A:
 | 
				
			||||||
 | 
					        case 0x0B: /* IE only */
 | 
				
			||||||
 | 
					        case 0x0C:
 | 
				
			||||||
 | 
					        case 0x0D: /* IE only */
 | 
				
			||||||
 | 
					            hs->pos += 1;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            return ch;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return CHAR_EOF;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_eof(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* eliminate unused function argument warning */
 | 
				
			||||||
 | 
					    (void)hs;
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_data(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    assert(hs->len >= hs->pos);
 | 
				
			||||||
 | 
					    idx = (const char*) memchr(hs->s + hs->pos, CHAR_LT, hs->len - hs->pos);
 | 
				
			||||||
 | 
					    if (idx == NULL) {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					        hs->token_type = DATA_TEXT;
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					        if (hs->token_len == 0) {
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_type = DATA_TEXT;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					        hs->state = h5_state_tag_open;
 | 
				
			||||||
 | 
					        if (hs->token_len == 0) {
 | 
				
			||||||
 | 
					            return h5_state_tag_open(hs);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12 2.4.8
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_tag_open(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    if (hs->pos >= hs->len) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ch = hs->s[hs->pos];
 | 
				
			||||||
 | 
					    if (ch == CHAR_BANG) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_markup_declaration_open(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_SLASH) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        hs->is_close = 1;
 | 
				
			||||||
 | 
					        return h5_state_end_tag_open(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_QUESTION) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_bogus_comment(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_PERCENT) {
 | 
				
			||||||
 | 
					        /* this is not in spec.. alternative comment format used
 | 
				
			||||||
 | 
					           by IE <= 9 and Safari < 4.0.3 */
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_bogus_comment2(hs);
 | 
				
			||||||
 | 
					    } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
 | 
				
			||||||
 | 
					        return h5_state_tag_name(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_NULL) {
 | 
				
			||||||
 | 
					        /* IE-ism  NULL characters are ignored */
 | 
				
			||||||
 | 
					        return h5_state_tag_name(hs);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* user input mistake in configuring state */
 | 
				
			||||||
 | 
					        if (hs->pos == 0) {
 | 
				
			||||||
 | 
					            return h5_state_data(hs);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos - 1;
 | 
				
			||||||
 | 
					        hs->token_len = 1;
 | 
				
			||||||
 | 
					        hs->token_type = DATA_TEXT;
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.9
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_end_tag_open(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (hs->pos >= hs->len) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ch = hs->s[hs->pos];
 | 
				
			||||||
 | 
					    if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					        return h5_state_data(hs);
 | 
				
			||||||
 | 
					    } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
 | 
				
			||||||
 | 
					        return h5_state_tag_name(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hs->is_close = 0;
 | 
				
			||||||
 | 
					    return h5_state_bogus_comment(hs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_tag_name_close(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    hs->is_close = 0;
 | 
				
			||||||
 | 
					    hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					    hs->token_len = 1;
 | 
				
			||||||
 | 
					    hs->token_type = TAG_NAME_CLOSE;
 | 
				
			||||||
 | 
					    hs->pos += 1;
 | 
				
			||||||
 | 
					    if (hs->pos < hs->len) {
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.10
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_tag_name(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos;
 | 
				
			||||||
 | 
					    while (pos < hs->len) {
 | 
				
			||||||
 | 
					        ch = hs->s[pos];
 | 
				
			||||||
 | 
					        if (ch == 0) {
 | 
				
			||||||
 | 
					            /* special non-standard case */
 | 
				
			||||||
 | 
					            /* allow nulls in tag name   */
 | 
				
			||||||
 | 
					            /* some old browsers apparently allow and ignore them */
 | 
				
			||||||
 | 
					            pos += 1;
 | 
				
			||||||
 | 
					        } else if (h5_is_white(ch)) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_NAME_OPEN;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            hs->state = h5_state_before_attribute_name;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_SLASH) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_NAME_OPEN;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            hs->state = h5_state_self_closing_start_tag;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = pos - hs->pos;
 | 
				
			||||||
 | 
					            if (hs->is_close) {
 | 
				
			||||||
 | 
					                hs->pos = pos + 1;
 | 
				
			||||||
 | 
					                hs->is_close = 0;
 | 
				
			||||||
 | 
					                hs->token_type = TAG_CLOSE;
 | 
				
			||||||
 | 
					                hs->state = h5_state_data;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                hs->pos = pos;
 | 
				
			||||||
 | 
					                hs->token_type = TAG_NAME_OPEN;
 | 
				
			||||||
 | 
					                hs->state = h5_state_tag_name_close;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            pos += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					    hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					    hs->token_type = TAG_NAME_OPEN;
 | 
				
			||||||
 | 
					    hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.34
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_before_attribute_name(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* for manual tail call optimization, see comment below */
 | 
				
			||||||
 | 
					    tail_call:;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ch = h5_skip_white(hs);
 | 
				
			||||||
 | 
					    switch (ch) {
 | 
				
			||||||
 | 
					    case CHAR_EOF: {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case CHAR_SLASH: {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        /* Logically, We want to call h5_state_self_closing_start_tag(hs) here.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           As this function may call us back and the compiler
 | 
				
			||||||
 | 
					           might not implement automatic tail call optimization,
 | 
				
			||||||
 | 
					           this might result in a deep recursion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           We detect this case here and start over with the current state.
 | 
				
			||||||
 | 
					        */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (hs->pos < hs->len && hs->s[hs->pos] != CHAR_GT) {
 | 
				
			||||||
 | 
					            goto tail_call;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return h5_state_self_closing_start_tag(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case CHAR_GT: {
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = 1;
 | 
				
			||||||
 | 
					        hs->token_type = TAG_NAME_CLOSE;
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default: {
 | 
				
			||||||
 | 
					        return h5_state_attribute_name(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_attribute_name(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos + 1;
 | 
				
			||||||
 | 
					    while (pos < hs->len) {
 | 
				
			||||||
 | 
					        ch = hs->s[pos];
 | 
				
			||||||
 | 
					        if (h5_is_white(ch)) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len   = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type  = ATTR_NAME;
 | 
				
			||||||
 | 
					            hs->state = h5_state_after_attribute_name;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_SLASH) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len   = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type  = ATTR_NAME;
 | 
				
			||||||
 | 
					            hs->state = h5_state_self_closing_start_tag;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_EQUALS) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len   = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type  = ATTR_NAME;
 | 
				
			||||||
 | 
					            hs->state = h5_state_before_attribute_value;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len   = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type  = ATTR_NAME;
 | 
				
			||||||
 | 
					            hs->state = h5_state_tag_name_close;
 | 
				
			||||||
 | 
					            hs->pos = pos;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            pos += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /* EOF */
 | 
				
			||||||
 | 
					    hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					    hs->token_len   = hs->len - hs->pos;
 | 
				
			||||||
 | 
					    hs->token_type  = ATTR_NAME;
 | 
				
			||||||
 | 
					    hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    hs->pos = hs->len;
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.36
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_after_attribute_name(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    c = h5_skip_white(hs);
 | 
				
			||||||
 | 
					    switch (c) {
 | 
				
			||||||
 | 
					    case CHAR_EOF: {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case CHAR_SLASH: {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_self_closing_start_tag(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case CHAR_EQUALS: {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_before_attribute_value(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case CHAR_GT: {
 | 
				
			||||||
 | 
					        return h5_state_tag_name_close(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default: {
 | 
				
			||||||
 | 
					        return h5_state_attribute_name(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.37
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_before_attribute_value(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int c;
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    c = h5_skip_white(hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (c == CHAR_EOF) {
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (c == CHAR_DOUBLE) {
 | 
				
			||||||
 | 
					        return h5_state_attribute_value_double_quote(hs);
 | 
				
			||||||
 | 
					    } else if (c == CHAR_SINGLE) {
 | 
				
			||||||
 | 
					        return h5_state_attribute_value_single_quote(hs);
 | 
				
			||||||
 | 
					    } else if (c == CHAR_TICK) {
 | 
				
			||||||
 | 
					        /* NON STANDARD IE */
 | 
				
			||||||
 | 
					        return h5_state_attribute_value_back_quote(hs);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return h5_state_attribute_value_no_quote(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_quote(h5_state_t* hs, char qchar)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* skip initial quote in normal case.
 | 
				
			||||||
 | 
					     * don't do this "if (pos == 0)" since it means we have started
 | 
				
			||||||
 | 
					     * in a non-data state.  given an input of '><foo
 | 
				
			||||||
 | 
					     * we want to make 0-length attribute name
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    if (hs->pos > 0) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    idx = (const char*) memchr(hs->s + hs->pos, qchar, hs->len - hs->pos);
 | 
				
			||||||
 | 
					    if (idx == NULL) {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					        hs->token_type = ATTR_VALUE;
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->token_type = ATTR_VALUE;
 | 
				
			||||||
 | 
					        hs->state = h5_state_after_attribute_value_quoted_state;
 | 
				
			||||||
 | 
					        hs->pos += hs->token_len + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
 | 
					int h5_state_attribute_value_double_quote(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    return h5_state_attribute_value_quote(hs, CHAR_DOUBLE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
 | 
					int h5_state_attribute_value_single_quote(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    return h5_state_attribute_value_quote(hs, CHAR_SINGLE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static
 | 
				
			||||||
 | 
					int h5_state_attribute_value_back_quote(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    return h5_state_attribute_value_quote(hs, CHAR_TICK);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_attribute_value_no_quote(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos;
 | 
				
			||||||
 | 
					    while (pos < hs->len) {
 | 
				
			||||||
 | 
					        ch = hs->s[pos];
 | 
				
			||||||
 | 
					        if (h5_is_white(ch)) {
 | 
				
			||||||
 | 
					            hs->token_type = ATTR_VALUE;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->pos = pos + 1;
 | 
				
			||||||
 | 
					            hs->state = h5_state_before_attribute_name;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					            hs->token_type = ATTR_VALUE;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = pos - hs->pos;
 | 
				
			||||||
 | 
					            hs->pos = pos;
 | 
				
			||||||
 | 
					            hs->state = h5_state_tag_name_close;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        pos += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    /* EOF */
 | 
				
			||||||
 | 
					    hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					    hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					    hs->token_type = ATTR_VALUE;
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.41
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_after_attribute_value_quoted_state(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    if (hs->pos >= hs->len) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ch = hs->s[hs->pos];
 | 
				
			||||||
 | 
					    if (h5_is_white(ch)) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_before_attribute_name(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_SLASH) {
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return h5_state_self_closing_start_tag(hs);
 | 
				
			||||||
 | 
					    } else if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = 1;
 | 
				
			||||||
 | 
					        hs->token_type = TAG_NAME_CLOSE;
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return h5_state_before_attribute_name(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.43
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  WARNING: This function is partially inlined into h5_state_before_attribute_name()
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_self_closing_start_tag(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    if (hs->pos >= hs->len) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ch = hs->s[hs->pos];
 | 
				
			||||||
 | 
					    if (ch == CHAR_GT) {
 | 
				
			||||||
 | 
					        assert(hs->pos > 0);
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos -1;
 | 
				
			||||||
 | 
					        hs->token_len = 2;
 | 
				
			||||||
 | 
					        hs->token_type = TAG_NAME_SELFCLOSE;
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        hs->pos += 1;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return h5_state_before_attribute_name(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.44
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_bogus_comment(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
 | 
				
			||||||
 | 
					    if (idx == NULL) {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos = hs->len;
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos =  (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.44 ALT
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_bogus_comment2(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos;
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        idx = (const char*) memchr(hs->s + pos, CHAR_PERCENT, hs->len - pos);
 | 
				
			||||||
 | 
					        if (idx == NULL || (idx + 1 >= hs->s + hs->len)) {
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->pos = hs->len;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (*(idx +1) != CHAR_GT) {
 | 
				
			||||||
 | 
					            pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* ends in %> */
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos = (size_t)(idx - hs->s) + 2;
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 8.2.4.45
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_markup_declaration_open(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    size_t remaining;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    remaining = hs->len - hs->pos;
 | 
				
			||||||
 | 
					    if (remaining >= 7 &&
 | 
				
			||||||
 | 
					        /* case insensitive */
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 0] == 'D' || hs->s[hs->pos + 0] == 'd') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 1] == 'O' || hs->s[hs->pos + 1] == 'o') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 2] == 'C' || hs->s[hs->pos + 2] == 'c') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 3] == 'T' || hs->s[hs->pos + 3] == 't') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 4] == 'Y' || hs->s[hs->pos + 4] == 'y') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 5] == 'P' || hs->s[hs->pos + 5] == 'p') &&
 | 
				
			||||||
 | 
					        (hs->s[hs->pos + 6] == 'E' || hs->s[hs->pos + 6] == 'e')
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					        return h5_state_doctype(hs);
 | 
				
			||||||
 | 
					    } else if (remaining >= 7 &&
 | 
				
			||||||
 | 
					               /* upper case required */
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 0] == '[' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 1] == 'C' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 2] == 'D' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 3] == 'A' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 4] == 'T' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 5] == 'A' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 6] == '['
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					        hs->pos += 7;
 | 
				
			||||||
 | 
					        return h5_state_cdata(hs);
 | 
				
			||||||
 | 
					    } else if (remaining >= 2 &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 0] == '-' &&
 | 
				
			||||||
 | 
					               hs->s[hs->pos + 1] == '-') {
 | 
				
			||||||
 | 
					        hs->pos += 2;
 | 
				
			||||||
 | 
					        return h5_state_comment(hs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return h5_state_bogus_comment(hs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 12.2.4.48
 | 
				
			||||||
 | 
					 * 12.2.4.49
 | 
				
			||||||
 | 
					 * 12.2.4.50
 | 
				
			||||||
 | 
					 * 12.2.4.51
 | 
				
			||||||
 | 
					 *   state machine spec is confusing since it can only look
 | 
				
			||||||
 | 
					 *   at one character at a time but simply it's comments end by:
 | 
				
			||||||
 | 
					 *   1) EOF
 | 
				
			||||||
 | 
					 *   2) ending in -->
 | 
				
			||||||
 | 
					 *   3) ending in -!>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_comment(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ch;
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					    size_t offset;
 | 
				
			||||||
 | 
					    const char* end = hs->s + hs->len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos;
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        idx = (const char*) memchr(hs->s + pos, CHAR_DASH, hs->len - pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* did not find anything or has less than 3 chars left */
 | 
				
			||||||
 | 
					        if (idx == NULL || idx > hs->s + hs->len - 3) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        offset = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* skip all nulls */
 | 
				
			||||||
 | 
					        while (idx + offset < end && *(idx + offset) == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (idx + offset == end) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ch = *(idx + offset);
 | 
				
			||||||
 | 
					        if (ch != CHAR_DASH && ch != CHAR_BANG) {
 | 
				
			||||||
 | 
					            pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* need to test */
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					        /* skip all nulls */
 | 
				
			||||||
 | 
					        while (idx + offset < end && *(idx + offset) == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (idx + offset == end) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        offset += 1;
 | 
				
			||||||
 | 
					        if (idx + offset == end) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ch = *(idx + offset);
 | 
				
			||||||
 | 
					        if (ch != CHAR_GT) {
 | 
				
			||||||
 | 
					            pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        offset += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* ends in --> or -!> */
 | 
				
			||||||
 | 
					        hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos = (size_t)(idx + offset - hs->s);
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        hs->token_type = TAG_COMMENT;
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int h5_state_cdata(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    pos = hs->pos;
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        idx = (const char*) memchr(hs->s + pos, CHAR_RIGHTB, hs->len - pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* did not find anything or has less than 3 chars left */
 | 
				
			||||||
 | 
					        if (idx == NULL || idx > hs->s + hs->len - 3) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					            hs->token_type = DATA_TEXT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if ( *(idx+1) == CHAR_RIGHTB && *(idx+2) == CHAR_GT) {
 | 
				
			||||||
 | 
					            hs->state = h5_state_data;
 | 
				
			||||||
 | 
					            hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					            hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					            hs->pos = (size_t)(idx - hs->s) + 3;
 | 
				
			||||||
 | 
					            hs->token_type = DATA_TEXT;
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 8.2.4.52
 | 
				
			||||||
 | 
					 * http://www.w3.org/html/wg/drafts/html/master/syntax.html#doctype-state
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int h5_state_doctype(h5_state_t* hs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TRACE();
 | 
				
			||||||
 | 
					    hs->token_start = hs->s + hs->pos;
 | 
				
			||||||
 | 
					    hs->token_type = DOCTYPE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    idx = (const char*) memchr(hs->s + hs->pos, CHAR_GT, hs->len - hs->pos);
 | 
				
			||||||
 | 
					    if (idx == NULL) {
 | 
				
			||||||
 | 
					        hs->state = h5_state_eof;
 | 
				
			||||||
 | 
					        hs->token_len = hs->len - hs->pos;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        hs->state = h5_state_data;
 | 
				
			||||||
 | 
					        hs->token_len = (size_t)(idx - hs->s) - hs->pos;
 | 
				
			||||||
 | 
					        hs->pos = (size_t)(idx - hs->s) + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					#ifndef LIBINJECTION_HTML5
 | 
				
			||||||
 | 
					#define LIBINJECTION_HTML5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* pull in size_t */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum html5_type {
 | 
				
			||||||
 | 
					    DATA_TEXT
 | 
				
			||||||
 | 
					    , TAG_NAME_OPEN
 | 
				
			||||||
 | 
					    , TAG_NAME_CLOSE
 | 
				
			||||||
 | 
					    , TAG_NAME_SELFCLOSE
 | 
				
			||||||
 | 
					    , TAG_DATA
 | 
				
			||||||
 | 
					    , TAG_CLOSE
 | 
				
			||||||
 | 
					    , ATTR_NAME
 | 
				
			||||||
 | 
					    , ATTR_VALUE
 | 
				
			||||||
 | 
					    , TAG_COMMENT
 | 
				
			||||||
 | 
					    , DOCTYPE
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum html5_flags {
 | 
				
			||||||
 | 
					  DATA_STATE
 | 
				
			||||||
 | 
					  , VALUE_NO_QUOTE
 | 
				
			||||||
 | 
					  , VALUE_SINGLE_QUOTE
 | 
				
			||||||
 | 
					  , VALUE_DOUBLE_QUOTE
 | 
				
			||||||
 | 
					  , VALUE_BACK_QUOTE
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct h5_state;
 | 
				
			||||||
 | 
					typedef int (*ptr_html5_state)(struct h5_state*);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct h5_state {
 | 
				
			||||||
 | 
					    const char* s;
 | 
				
			||||||
 | 
					    size_t len;
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					    int is_close;
 | 
				
			||||||
 | 
					    ptr_html5_state state;
 | 
				
			||||||
 | 
					    const char* token_start;
 | 
				
			||||||
 | 
					    size_t token_len;
 | 
				
			||||||
 | 
					    enum html5_type token_type;
 | 
				
			||||||
 | 
					} h5_state_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void libinjection_h5_init(h5_state_t* hs, const char* s, size_t len, enum html5_flags);
 | 
				
			||||||
 | 
					int libinjection_h5_next(h5_state_t* hs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										2336
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_sqli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2336
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_sqli.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										294
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_sqli.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_sqli.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,294 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Copyright 2012-2016 Nick Galbreath
 | 
				
			||||||
 | 
					 * nickg@client9.com
 | 
				
			||||||
 | 
					 * BSD License -- see `COPYING.txt` for details
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * https://libinjection.client9.com/
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef LIBINJECTION_SQLI_H
 | 
				
			||||||
 | 
					#define LIBINJECTION_SQLI_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Pull in size_t
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum sqli_flags {
 | 
				
			||||||
 | 
					    FLAG_NONE            = 0
 | 
				
			||||||
 | 
					    , FLAG_QUOTE_NONE    = 1   /* 1 << 0 */
 | 
				
			||||||
 | 
					    , FLAG_QUOTE_SINGLE  = 2   /* 1 << 1 */
 | 
				
			||||||
 | 
					    , FLAG_QUOTE_DOUBLE  = 4   /* 1 << 2 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    , FLAG_SQL_ANSI      = 8   /* 1 << 3 */
 | 
				
			||||||
 | 
					    , FLAG_SQL_MYSQL     = 16  /* 1 << 4 */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum lookup_type {
 | 
				
			||||||
 | 
					    LOOKUP_WORD        = 1
 | 
				
			||||||
 | 
					    , LOOKUP_TYPE        = 2
 | 
				
			||||||
 | 
					    , LOOKUP_OPERATOR    = 3
 | 
				
			||||||
 | 
					    , LOOKUP_FINGERPRINT = 4
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct libinjection_sqli_token {
 | 
				
			||||||
 | 
					#ifdef SWIG
 | 
				
			||||||
 | 
					%immutable;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * position and length of token
 | 
				
			||||||
 | 
					     * in original string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					    size_t len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*  count:
 | 
				
			||||||
 | 
					     *  in type 'v', used for number of opening '@'
 | 
				
			||||||
 | 
					     *  but maybe used in other contexts
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int  count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char type;
 | 
				
			||||||
 | 
					    char str_open;
 | 
				
			||||||
 | 
					    char str_close;
 | 
				
			||||||
 | 
					    char val[32];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct libinjection_sqli_token stoken_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Pointer to function, takes c-string input,
 | 
				
			||||||
 | 
					 *  returns '\0' for no match, else a char
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct libinjection_sqli_state;
 | 
				
			||||||
 | 
					typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct libinjection_sqli_state {
 | 
				
			||||||
 | 
					#ifdef SWIG
 | 
				
			||||||
 | 
					%immutable;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * input, does not need to be null terminated.
 | 
				
			||||||
 | 
					     * it is also not modified.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    const char *s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * input length
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    size_t slen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * How to lookup a word or fingerprint
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ptr_lookup_fn lookup;
 | 
				
			||||||
 | 
					    void*         userdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * pos is the index in the string during tokenization
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    size_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef SWIG
 | 
				
			||||||
 | 
					    /* for SWIG.. don't use this.. use functional API instead */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* MAX TOKENS + 1 since we use one extra token
 | 
				
			||||||
 | 
					     * to determine the type of the previous token
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    struct libinjection_sqli_token tokenvec[8];
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Pointer to token position in tokenvec, above
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    struct libinjection_sqli_token *current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * fingerprint pattern c-string
 | 
				
			||||||
 | 
					     * +1 for ending null
 | 
				
			||||||
 | 
					     * Minimum of 8 bytes to add gcc's -fstack-protector to work
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    char fingerprint[8];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * Line number of code that said decided if the input was SQLi or
 | 
				
			||||||
 | 
					     * not.  Most of the time it's line that said "it's not a matching
 | 
				
			||||||
 | 
					     * fingerprint" but there is other logic that sometimes approves
 | 
				
			||||||
 | 
					     * an input. This is only useful for debugging.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int reason;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Number of ddw (dash-dash-white) comments
 | 
				
			||||||
 | 
					     * These comments are in the form of
 | 
				
			||||||
 | 
					     *   '--[whitespace]' or '--[EOF]'
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * All databases treat this as a comment.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					     int stats_comment_ddw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Number of ddx (dash-dash-[notwhite]) comments
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * ANSI SQL treats these are comments, MySQL treats this as
 | 
				
			||||||
 | 
					     * two unary operators '-' '-'
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * If you are parsing result returns FALSE and
 | 
				
			||||||
 | 
					     * stats_comment_dd > 0, you should reparse with
 | 
				
			||||||
 | 
					     * COMMENT_MYSQL
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int stats_comment_ddx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * c-style comments found  /x .. x/
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int stats_comment_c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* '#' operators or MySQL EOL comments found
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int stats_comment_hash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * number of tokens folded away
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int stats_folds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * total tokens processed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int stats_tokens;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct libinjection_sqli_state sfilter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct libinjection_sqli_token* libinjection_sqli_get_token(
 | 
				
			||||||
 | 
					    struct libinjection_sqli_state* sql_state, int i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Version info.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is moved into a function to allow SWIG and other auto-generated
 | 
				
			||||||
 | 
					 * binding to not be modified during minor release changes.  We change
 | 
				
			||||||
 | 
					 * change the version number in the c source file, and not regenerated
 | 
				
			||||||
 | 
					 * the binding
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See python's normalized version
 | 
				
			||||||
 | 
					 * http://www.python.org/dev/peps/pep-0386/#normalizedversion
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const char* libinjection_version(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void libinjection_sqli_init(struct libinjection_sqli_state *sf,
 | 
				
			||||||
 | 
					                            const char* s, size_t len,
 | 
				
			||||||
 | 
					                            int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Main API: tests for SQLi in three possible contexts, no quotes,
 | 
				
			||||||
 | 
					 * single quote and double quote
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param sql_state core data structure
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \return 1 (true) if SQLi, 0 (false) if benign
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_is_sqli(struct libinjection_sqli_state* sql_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*  FOR HACKERS ONLY
 | 
				
			||||||
 | 
					 *   provides deep hooks into the decision making process
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void libinjection_sqli_callback(struct libinjection_sqli_state *sf,
 | 
				
			||||||
 | 
					                                ptr_lookup_fn fn,
 | 
				
			||||||
 | 
					                                void* userdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Resets state, but keeps initial string and callbacks
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void libinjection_sqli_reset(struct libinjection_sqli_state *sf,
 | 
				
			||||||
 | 
					                             int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This detects SQLi in a single context, mostly useful for custom
 | 
				
			||||||
 | 
					 * logic and debugging.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param sql_state  Main data structure
 | 
				
			||||||
 | 
					 * \param flags flags to adjust parsing
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \returns a pointer to sfilter.fingerprint as convenience
 | 
				
			||||||
 | 
					 *          do not free!
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state *sql_state,
 | 
				
			||||||
 | 
					                                          int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The default "word" to token-type or fingerprint function.  This
 | 
				
			||||||
 | 
					 * uses a ASCII case-insensitive binary tree.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char libinjection_sqli_lookup_word(struct libinjection_sqli_state *sql_state,
 | 
				
			||||||
 | 
					                                   int lookup_type,
 | 
				
			||||||
 | 
					                                   const char* str,
 | 
				
			||||||
 | 
					                                   size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Streaming tokenization interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * sql_state->current is updated with the current token.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \returns 1, has a token, keep going, or 0 no tokens
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int  libinjection_sqli_tokenize(struct libinjection_sqli_state *sf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * parses and folds input, up to 5 tokens
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_sqli_fold(struct libinjection_sqli_state *sf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** The built-in default function to match fingerprints
 | 
				
			||||||
 | 
					 *  and do false negative/positive analysis.  This calls the following
 | 
				
			||||||
 | 
					 *  two functions.  With this, you over-ride one part or the other.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *     return libinjection_sqli_blacklist(sql_state) &&
 | 
				
			||||||
 | 
					 *        libinjection_sqli_not_whitelist(sql_state);
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param sql_state should be filled out after libinjection_sqli_fingerprint is called
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Given a pattern determine if it's a SQLi pattern.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \return TRUE if sqli, false otherwise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_sqli_blacklist(struct libinjection_sqli_state* sql_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Given a positive match for a pattern (i.e. pattern is SQLi), this function
 | 
				
			||||||
 | 
					 * does additional analysis to reduce false positives.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \return TRUE if SQLi, false otherwise
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_sqli_not_whitelist(struct libinjection_sqli_state * sql_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* LIBINJECTION_SQLI_H */
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										918
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_xss.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										918
									
								
								internal/waf/injectionutils/libinjection/src/libinjection_xss.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,918 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection.h"
 | 
				
			||||||
 | 
					#include "libinjection_xss.h"
 | 
				
			||||||
 | 
					#include "libinjection_html5.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum attribute {
 | 
				
			||||||
 | 
					    TYPE_NONE
 | 
				
			||||||
 | 
					    , TYPE_BLACK     /* ban always */
 | 
				
			||||||
 | 
					    , TYPE_ATTR_URL   /* attribute value takes a URL-like object */
 | 
				
			||||||
 | 
					    , TYPE_STYLE
 | 
				
			||||||
 | 
					    , TYPE_ATTR_INDIRECT  /* attribute *name* is given in *value* */
 | 
				
			||||||
 | 
					} attribute_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static attribute_t is_black_attr(const char* s, size_t len, int strictMode);
 | 
				
			||||||
 | 
					static int is_black_tag(const char* s, size_t len, int strictMode);
 | 
				
			||||||
 | 
					static int is_black_url(const char* s, size_t len);
 | 
				
			||||||
 | 
					static int cstrcasecmp_with_null(const char *a, const char *b, size_t n);
 | 
				
			||||||
 | 
					static int html_decode_char_at(const char* src, size_t len, size_t* consumed);
 | 
				
			||||||
 | 
					static int htmlencode_startswith(const char *a/* prefix */, const char *b /* src */, size_t n);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct stringtype {
 | 
				
			||||||
 | 
					    const char* name;
 | 
				
			||||||
 | 
					    attribute_t atype;
 | 
				
			||||||
 | 
					} stringtype_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int gsHexDecodeMap[256] = {
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    0,   1,   2,   3,   4,   5,   6,   7,   8,   9, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256,  10,  11,  12,  13,  14,  15, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256,  10,  11,  12,  13,  14,  15, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
 | 
				
			||||||
 | 
					    256, 256, 256, 256
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int html_decode_char_at(const char* src, size_t len, size_t* consumed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int val = 0;
 | 
				
			||||||
 | 
					    size_t i;
 | 
				
			||||||
 | 
					    int ch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len == 0 || src == NULL) {
 | 
				
			||||||
 | 
					        *consumed = 0;
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *consumed = 1;
 | 
				
			||||||
 | 
					    if (*src != '&' || len < 2) {
 | 
				
			||||||
 | 
					        return (unsigned char)(*src);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (*(src+1) != '#') {
 | 
				
			||||||
 | 
					        /* normally this would be for named entities
 | 
				
			||||||
 | 
					         * but for this case we don't actually care
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        return '&';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (*(src+2) == 'x' || *(src+2) == 'X') {
 | 
				
			||||||
 | 
					        ch = (unsigned char) (*(src+3));
 | 
				
			||||||
 | 
					        ch = gsHexDecodeMap[ch];
 | 
				
			||||||
 | 
					        if (ch == 256) {
 | 
				
			||||||
 | 
					            /* degenerate case  '&#[?]' */
 | 
				
			||||||
 | 
					            return '&';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        val = ch;
 | 
				
			||||||
 | 
					        i = 4;
 | 
				
			||||||
 | 
					        while (i < len) {
 | 
				
			||||||
 | 
					            ch = (unsigned char) src[i];
 | 
				
			||||||
 | 
					            if (ch == ';') {
 | 
				
			||||||
 | 
					                *consumed = i + 1;
 | 
				
			||||||
 | 
					                return val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ch = gsHexDecodeMap[ch];
 | 
				
			||||||
 | 
					            if (ch == 256) {
 | 
				
			||||||
 | 
					                *consumed = i;
 | 
				
			||||||
 | 
					                return val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val = (val * 16) + ch;
 | 
				
			||||||
 | 
					            if (val > 0x1000FF) {
 | 
				
			||||||
 | 
					                return '&';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++i;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        *consumed = i;
 | 
				
			||||||
 | 
					        return val;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        i = 2;
 | 
				
			||||||
 | 
					        ch = (unsigned char) src[i];
 | 
				
			||||||
 | 
					        if (ch < '0' || ch > '9') {
 | 
				
			||||||
 | 
					            return '&';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        val = ch - '0';
 | 
				
			||||||
 | 
					        i += 1;
 | 
				
			||||||
 | 
					        while (i < len) {
 | 
				
			||||||
 | 
					            ch = (unsigned char) src[i];
 | 
				
			||||||
 | 
					            if (ch == ';') {
 | 
				
			||||||
 | 
					                *consumed = i + 1;
 | 
				
			||||||
 | 
					                return val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (ch < '0' || ch > '9') {
 | 
				
			||||||
 | 
					                *consumed = i;
 | 
				
			||||||
 | 
					                return val;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val = (val * 10) + (ch - '0');
 | 
				
			||||||
 | 
					            if (val > 0x1000FF) {
 | 
				
			||||||
 | 
					                return '&';
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++i;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        *consumed = i;
 | 
				
			||||||
 | 
					        return val;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * These were mostly extracted from: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/WebCore/dom/EventNames.h
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * view-source:
 | 
				
			||||||
 | 
					 * data:
 | 
				
			||||||
 | 
					 * javascript:
 | 
				
			||||||
 | 
					 * events:
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static stringtype_t BLACKATTREVENT[] = {
 | 
				
			||||||
 | 
					   { "ABORT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ACTIVATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ACTIVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ADDSOURCEBUFFER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ADDSTREAM", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ADDTRACK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AFTERPRINT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ANIMATIONCANCEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ANIMATIONEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ANIMATIONITERATION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ANIMATIONSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AUDIOEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AUDIOPROCESS", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AUDIOSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AUTOCOMPLETEERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "AUTOCOMPLETE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFOREACTIVATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFORECOPY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFORECUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFOREINPUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFORELOAD", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFOREPASTE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFOREPRINT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEFOREUNLOAD", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BEGINEVENT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BLOCKED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BLUR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BOUNDARY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "BUFFEREDAMOUNTLOW", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CACHED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CANCEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CANPLAYTHROUGH", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CANPLAY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CHARGINGCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CHARGINGTIMECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CHECKING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CLICK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CLOSE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "COMPLETE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "COMPOSITIONEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "COMPOSITIONSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "COMPOSITIONUPDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CONNECTING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CONNECTIONSTATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CONNECT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CONTEXTMENU", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CONTROLLERCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "COPY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CUECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "CUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DATAAVAILABLE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DATACHANNEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DBLCLICK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DEVICECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DEVICEMOTION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DEVICEORIENTATION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DISCHARGINGTIMECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DISCONNECT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMACTIVATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMCHARACTERDATAMODIFIED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMCONTENTLOADED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMFOCUSIN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMFOCUSOUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMNODEINSERTEDINTODOCUMENT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMNODEINSERTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMNODEREMOVEDFROMDOCUMENT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMNODEREMOVED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOMSUBTREEMODIFIED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DOWNLOADING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAGEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAGENTER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAGLEAVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAGOVER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAGSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DRAG", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DROP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "DURATIONCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "EMPTIED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ENCRYPTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ENDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ENDEVENT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "END", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ENTERPICTUREINPICTURE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ENTER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "EXIT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FETCH", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FINISH", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FOCUSIN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FOCUSOUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FOCUS", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FORMCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "FORMINPUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GAMEPADCONNECTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GAMEPADDISCONNECTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTUREEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURESCROLLEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURESCROLLSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURESCROLLUPDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURESTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURETAPDOWN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GESTURETAP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "GOTPOINTERCAPTURE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "HASHCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ICECANDIDATEERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ICECANDIDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ICECONNECTIONSTATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ICEGATHERINGSTATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "INACTIVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "INPUTSOURCESCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "INPUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "INSTALL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "INVALID", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "KEYDOWN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "KEYPRESS", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "KEYSTATUSESCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "KEYUP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LANGUAGECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LEAVEPICTUREINPICTURE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LEVELCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADEDDATA", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADEDMETADATA", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADINGDONE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADINGERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOADSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOAD", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "LOSTPOINTERCAPTURE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MARK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MERCHANTVALIDATION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MESSAGEERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MESSAGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEDOWN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEENTER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSELEAVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEMOVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEOUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEOVER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEUP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MOUSEWHEEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "MUTE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "NEGOTIATIONNEEDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "NEXTTRACK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "NOMATCH", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "NOUPDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "OBSOLETE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "OFFLINE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ONLINE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "OPEN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ORIENTATIONCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "OVERCONSTRAINED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "OVERFLOWCHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAGEHIDE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAGESHOW", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PASTE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAUSE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAYERDETAILCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAYMENTAUTHORIZED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAYMENTMETHODCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PAYMENTMETHODSELECTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PLAYING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PLAY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERCANCEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERDOWN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERENTER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERLEAVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERLOCKCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERLOCKERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERMOVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTEROUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTEROVER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POINTERUP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "POPSTATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PREVIOUSTRACK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PROCESSORERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PROGRESS", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "PROPERTYCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "READYSTATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "REJECTIONHANDLED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "REMOVESOURCEBUFFER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "REMOVESTREAM", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "REMOVETRACK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "REMOVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RESET", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RESIZE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RESOURCETIMINGBUFFERFULL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RESULT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "RESUME", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SCROLL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SEARCH", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SECURITYPOLICYVIOLATION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SEEKED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SEEKING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SELECTEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SELECTIONCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SELECTSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SELECT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SHIPPINGADDRESSCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SHIPPINGCONTACTSELECTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SHIPPINGMETHODSELECTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SHIPPINGOPTIONCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SHOW", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SIGNALINGSTATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SLOTCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SOUNDEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SOUNDSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SOURCECLOSE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SOURCEENDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SOURCEOPEN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SPEECHEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SPEECHSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SQUEEZEEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SQUEEZESTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SQUEEZE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "STALLED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "STARTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "START", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "STATECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "STOP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "STORAGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SUBMIT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SUCCESS", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "SUSPEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TEXTINPUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TIMEOUT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TIMEUPDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOGGLE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOGGLE", TYPE_BLACK }	
 | 
				
			||||||
 | 
					 , { "TONECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOUCHCANCEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOUCHEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOUCHFORCECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOUCHMOVE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TOUCHSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TRACK", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TRANSITIONCANCEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TRANSITIONEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TRANSITIONRUN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "TRANSITIONSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UNCAPTUREDERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UNHANDLEDREJECTION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UNLOAD", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UNMUTE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPDATEEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPDATEFOUND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPDATEREADY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPDATESTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPDATE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "UPGRADENEEDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "VALIDATEMERCHANT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "VERSIONCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "VISIBILITYCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "VOLUMECHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WAITINGFORKEY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WAITING", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBGLCONTEXTCHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBGLCONTEXTCREATIONERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBGLCONTEXTLOST", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBGLCONTEXTRESTORED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITANIMATIONEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITANIMATIONITERATION", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITANIMATIONSTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITBEFORETEXTINSERTED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITBEGINFULLSCREEN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITCURRENTPLAYBACKTARGETISWIRELESSCHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITENDFULLSCREEN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITFULLSCREENCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITFULLSCREENERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITKEYADDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITKEYERROR", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITKEYMESSAGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITMOUSEFORCECHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITMOUSEFORCEDOWN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITMOUSEFORCEUP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITMOUSEFORCEWILLBEGIN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITNEEDKEY", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITNETWORKINFOCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITPLAYBACKTARGETAVAILABILITYCHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITPRESENTATIONMODECHANGED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITREGIONOVERSETCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITREMOVESOURCEBUFFER", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITSOURCECLOSE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITSOURCEENDED", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITSOURCEOPEN", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITSPEECHCHANGE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITTRANSITIONEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITWILLREVEALBOTTOM", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITWILLREVEALLEFT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITWILLREVEALRIGHT", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WEBKITWILLREVEALTOP", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WHEEL", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WRITEEND", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WRITESTART", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "WRITE", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { "ZOOM", TYPE_BLACK }
 | 
				
			||||||
 | 
					 , { NULL, TYPE_NONE }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * view-source:
 | 
				
			||||||
 | 
					 * data:
 | 
				
			||||||
 | 
					 * javascript:
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static stringtype_t STRICT_BLACKATTR[] = {
 | 
				
			||||||
 | 
					    { "ACTION", TYPE_ATTR_URL }     /* form */
 | 
				
			||||||
 | 
					    , { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
 | 
				
			||||||
 | 
					    , { "BY", TYPE_ATTR_URL }         /* SVG */
 | 
				
			||||||
 | 
					    , { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
 | 
				
			||||||
 | 
					    , { "DATAFORMATAS", TYPE_BLACK }  /* IE */
 | 
				
			||||||
 | 
					    , { "DATASRC", TYPE_BLACK }       /* IE */
 | 
				
			||||||
 | 
					    , { "DYNSRC", TYPE_ATTR_URL }     /* Obsolete img attribute */
 | 
				
			||||||
 | 
					    , { "FILTER", TYPE_STYLE }        /* Opera, SVG inline style */
 | 
				
			||||||
 | 
					    , { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
 | 
				
			||||||
 | 
					    , { "FOLDER", TYPE_ATTR_URL }     /* Only on A tags, IE-only */
 | 
				
			||||||
 | 
					    , { "FROM", TYPE_ATTR_URL }       /* SVG */
 | 
				
			||||||
 | 
					    , { "HANDLER", TYPE_ATTR_URL }    /* SVG Tiny, Opera */
 | 
				
			||||||
 | 
					    , { "HREF", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { "LOWSRC", TYPE_ATTR_URL }     /* Obsolete img attribute */
 | 
				
			||||||
 | 
					    , { "POSTER", TYPE_ATTR_URL }     /* Opera 10,11 */
 | 
				
			||||||
 | 
					    , { "SRC", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { "STYLE", TYPE_STYLE }
 | 
				
			||||||
 | 
					    , { "TO", TYPE_ATTR_URL }         /* SVG */
 | 
				
			||||||
 | 
					    , { "VALUES", TYPE_ATTR_URL }     /* SVG */
 | 
				
			||||||
 | 
					    , { "XLINK:HREF", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { NULL, TYPE_NONE }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static stringtype_t BLACKATTR[] = {
 | 
				
			||||||
 | 
					    { "ACTION", TYPE_ATTR_URL }     /* form */
 | 
				
			||||||
 | 
					    , { "ATTRIBUTENAME", TYPE_ATTR_INDIRECT } /* SVG allow indirection of attribute names */
 | 
				
			||||||
 | 
					    , { "BY", TYPE_ATTR_URL }         /* SVG */
 | 
				
			||||||
 | 
					    , { "BACKGROUND", TYPE_ATTR_URL } /* IE6, O11 */
 | 
				
			||||||
 | 
					    , { "DATAFORMATAS", TYPE_BLACK }  /* IE */
 | 
				
			||||||
 | 
					    , { "DATASRC", TYPE_BLACK }       /* IE */
 | 
				
			||||||
 | 
					    , { "DYNSRC", TYPE_ATTR_URL }     /* Obsolete img attribute */
 | 
				
			||||||
 | 
					    , { "FILTER", TYPE_STYLE }        /* Opera, SVG inline style */
 | 
				
			||||||
 | 
					    , { "FORMACTION", TYPE_ATTR_URL } /* HTML 5 */
 | 
				
			||||||
 | 
					    , { "FOLDER", TYPE_ATTR_URL }     /* Only on A tags, IE-only */
 | 
				
			||||||
 | 
					    , { "FROM", TYPE_ATTR_URL }       /* SVG */
 | 
				
			||||||
 | 
					    , { "HANDLER", TYPE_ATTR_URL }    /* SVG Tiny, Opera */
 | 
				
			||||||
 | 
					    , { "HREF", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { "LOWSRC", TYPE_ATTR_URL }     /* Obsolete img attribute */
 | 
				
			||||||
 | 
					    , { "POSTER", TYPE_ATTR_URL }     /* Opera 10,11 */
 | 
				
			||||||
 | 
					    , { "SRC", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { "TO", TYPE_ATTR_URL }         /* SVG */
 | 
				
			||||||
 | 
					    , { "VALUES", TYPE_ATTR_URL }     /* SVG */
 | 
				
			||||||
 | 
					    , { "XLINK:HREF", TYPE_ATTR_URL }
 | 
				
			||||||
 | 
					    , { NULL, TYPE_NONE }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* xmlns */
 | 
				
			||||||
 | 
					/* `xml-stylesheet` > <eval>, <if expr=> */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  static const char* BLACKATTR[] = {
 | 
				
			||||||
 | 
					  "ATTRIBUTENAME",
 | 
				
			||||||
 | 
					  "BACKGROUND",
 | 
				
			||||||
 | 
					  "DATAFORMATAS",
 | 
				
			||||||
 | 
					  "HREF",
 | 
				
			||||||
 | 
					  "SCROLL",
 | 
				
			||||||
 | 
					  "SRC",
 | 
				
			||||||
 | 
					  "STYLE",
 | 
				
			||||||
 | 
					  "SRCDOC",
 | 
				
			||||||
 | 
					  NULL
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GoEdge: change BLACKTAG to STRICT_BLACKTAG
 | 
				
			||||||
 | 
					static const char* STRICT_BLACKTAG[] = {
 | 
				
			||||||
 | 
					    "APPLET"
 | 
				
			||||||
 | 
					    , "AUDIO"
 | 
				
			||||||
 | 
					    , "BASE"
 | 
				
			||||||
 | 
					    , "COMMENT"  /* IE http://html5sec.org/#38 */
 | 
				
			||||||
 | 
					    , "EMBED"
 | 
				
			||||||
 | 
					    , "FORM"
 | 
				
			||||||
 | 
					    , "FRAME"
 | 
				
			||||||
 | 
					    , "FRAMESET"
 | 
				
			||||||
 | 
					    , "HANDLER" /* Opera SVG, effectively a script tag */
 | 
				
			||||||
 | 
					    , "IFRAME"
 | 
				
			||||||
 | 
					    , "IMPORT"
 | 
				
			||||||
 | 
					    , "ISINDEX"
 | 
				
			||||||
 | 
					    , "LINK"
 | 
				
			||||||
 | 
					    , "LISTENER"
 | 
				
			||||||
 | 
					    /*    , "MARQUEE" */
 | 
				
			||||||
 | 
					    , "META"
 | 
				
			||||||
 | 
					    , "NOSCRIPT"
 | 
				
			||||||
 | 
					    , "OBJECT"
 | 
				
			||||||
 | 
					    , "SCRIPT"
 | 
				
			||||||
 | 
					    , "STYLE"
 | 
				
			||||||
 | 
					    , "VIDEO"
 | 
				
			||||||
 | 
					    , "VMLFRAME"
 | 
				
			||||||
 | 
					    , "XML"
 | 
				
			||||||
 | 
					    , "XSS"
 | 
				
			||||||
 | 
					    , NULL
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* BLACKTAG[] = {
 | 
				
			||||||
 | 
					    "APPLET"
 | 
				
			||||||
 | 
					    /*    , "AUDIO" */
 | 
				
			||||||
 | 
					    , "BASE"
 | 
				
			||||||
 | 
					    , "COMMENT"  /* IE http://html5sec.org/#38 */
 | 
				
			||||||
 | 
					    , "EMBED"
 | 
				
			||||||
 | 
					    /*   ,  "FORM" */
 | 
				
			||||||
 | 
					    , "FRAME"
 | 
				
			||||||
 | 
					    , "FRAMESET"
 | 
				
			||||||
 | 
					    , "HANDLER" /* Opera SVG, effectively a script tag */
 | 
				
			||||||
 | 
					    , "IFRAME"
 | 
				
			||||||
 | 
					    , "IMPORT"
 | 
				
			||||||
 | 
					    , "ISINDEX"
 | 
				
			||||||
 | 
					    , "LINK"
 | 
				
			||||||
 | 
					    , "LISTENER"
 | 
				
			||||||
 | 
					    /*    , "MARQUEE" */
 | 
				
			||||||
 | 
					    , "META"
 | 
				
			||||||
 | 
					    , "NOSCRIPT"
 | 
				
			||||||
 | 
					    , "OBJECT"
 | 
				
			||||||
 | 
					    , "SCRIPT"
 | 
				
			||||||
 | 
					    , "STYLE"
 | 
				
			||||||
 | 
					    /*    , "VIDEO" */
 | 
				
			||||||
 | 
					    , "VMLFRAME"
 | 
				
			||||||
 | 
					    , "XSS"
 | 
				
			||||||
 | 
					    , NULL
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int cstrcasecmp_with_null(const char *a, const char *b, size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char ca;
 | 
				
			||||||
 | 
					    char cb;
 | 
				
			||||||
 | 
					    /* printf("Comparing to %s %.*s\n", a, (int)n, b); */
 | 
				
			||||||
 | 
					    while (n-- > 0) {
 | 
				
			||||||
 | 
					        cb = *b++;
 | 
				
			||||||
 | 
					        if (cb == '\0') continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ca = *a++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cb >= 'a' && cb <= 'z') {
 | 
				
			||||||
 | 
					            cb -= 0x20;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* printf("Comparing %c vs %c with %d left\n", ca, cb, (int)n); */
 | 
				
			||||||
 | 
					        if (ca != cb) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (*a == 0) {
 | 
				
			||||||
 | 
					        /* printf(" MATCH \n"); */
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Does an HTML encoded  binary string (const char*, length) start with
 | 
				
			||||||
 | 
					 * a all uppercase c-string (null terminated), case insensitive!
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * also ignore any embedded nulls in the HTML string!
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * return 1 if match / starts with
 | 
				
			||||||
 | 
					 * return 0 if not
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int htmlencode_startswith(const char *a, const char *b, size_t n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    size_t consumed;
 | 
				
			||||||
 | 
					    int cb;
 | 
				
			||||||
 | 
					    int first = 1;
 | 
				
			||||||
 | 
					    /* printf("Comparing %s with %.*s\n", a,(int)n,b); */
 | 
				
			||||||
 | 
					    while (n > 0) {
 | 
				
			||||||
 | 
					        if (*a == 0) {
 | 
				
			||||||
 | 
					            /* printf("Match EOL!\n"); */
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        cb = html_decode_char_at(b, n, &consumed);
 | 
				
			||||||
 | 
					        b += consumed;
 | 
				
			||||||
 | 
					        n -= consumed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (first && cb <= 32) {
 | 
				
			||||||
 | 
					            /* ignore all leading whitespace and control characters */
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        first = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cb == 0) {
 | 
				
			||||||
 | 
					            /* always ignore null characters in user input */
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cb == 10) {
 | 
				
			||||||
 | 
					            /* always ignore vertical tab characters in user input */
 | 
				
			||||||
 | 
					            /* who allows this?? */
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (cb >= 'a' && cb <= 'z') {
 | 
				
			||||||
 | 
					            /* upcase */
 | 
				
			||||||
 | 
					            cb -= 0x20;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (*a != (char) cb) {
 | 
				
			||||||
 | 
					            /* printf("    %c != %c\n", *a, cb); */
 | 
				
			||||||
 | 
					            /* mismatch */
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        a++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (*a == 0) ? 1 : 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int is_black_tag(const char* s, size_t len, int strictMode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char** black;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len < 3) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strictMode == 1) {
 | 
				
			||||||
 | 
					    	black = STRICT_BLACKTAG;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    	black = BLACKTAG;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (*black != NULL) {
 | 
				
			||||||
 | 
					        if (cstrcasecmp_with_null(*black, s, len) == 0) {
 | 
				
			||||||
 | 
					            /* printf("Got black tag %s\n", *black); */
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        black += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* anything SVG related */
 | 
				
			||||||
 | 
					    if ((s[0] == 's' || s[0] == 'S') &&
 | 
				
			||||||
 | 
					        (s[1] == 'v' || s[1] == 'V') &&
 | 
				
			||||||
 | 
					        (s[2] == 'g' || s[2] == 'G')) {
 | 
				
			||||||
 | 
					        /*        printf("Got SVG tag \n"); */
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Anything XSL(t) related */
 | 
				
			||||||
 | 
					    if ((s[0] == 'x' || s[0] == 'X') &&
 | 
				
			||||||
 | 
					        (s[1] == 's' || s[1] == 'S') &&
 | 
				
			||||||
 | 
					        (s[2] == 'l' || s[2] == 'L')) {
 | 
				
			||||||
 | 
					        /*      printf("Got XSL tag\n"); */
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static attribute_t is_black_attr(const char* s, size_t len, int strictMode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    stringtype_t* black;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len < 2) {
 | 
				
			||||||
 | 
					        return TYPE_NONE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len >= 5) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* JavaScript on.* event handlers */
 | 
				
			||||||
 | 
					        if ((s[0] == 'o' || s[0] == 'O') && (s[1] == 'n' || s[1] == 'N')) {
 | 
				
			||||||
 | 
					            black = BLACKATTREVENT;
 | 
				
			||||||
 | 
					            const char *s_without_on = &s[2]; // start comparing from the third char
 | 
				
			||||||
 | 
					            while (black->name != NULL) {
 | 
				
			||||||
 | 
					                if (cstrcasecmp_with_null(black->name, s_without_on, strlen(black->name)) == 0) {
 | 
				
			||||||
 | 
					                    /* printf("Got banned attribute name %s\n", black->name); */
 | 
				
			||||||
 | 
					                    return black->atype;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                black += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* XMLNS can be used to create arbitrary tags */
 | 
				
			||||||
 | 
					        // goedge: commented for photo uploading
 | 
				
			||||||
 | 
					        //if (cstrcasecmp_with_null("XMLNS", s, 5) == 0 || cstrcasecmp_with_null("XLINK", s, 5) == 0) {
 | 
				
			||||||
 | 
					            /*      printf("Got XMLNS and XLINK tags\n"); */
 | 
				
			||||||
 | 
					        //    return TYPE_BLACK;
 | 
				
			||||||
 | 
					        //}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strictMode == 1) {
 | 
				
			||||||
 | 
					    	black = STRICT_BLACKATTR;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    	black = BLACKATTR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (black->name != NULL) {
 | 
				
			||||||
 | 
					        if (cstrcasecmp_with_null(black->name, s, len) == 0) {
 | 
				
			||||||
 | 
					            /*      printf("Got banned attribute name %s\n", black->name); */
 | 
				
			||||||
 | 
					            return black->atype;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        black += 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return TYPE_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int is_black_url(const char* s, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const char* data_url = "DATA";
 | 
				
			||||||
 | 
					    static const char* viewsource_url = "VIEW-SOURCE";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* obsolete but interesting signal */
 | 
				
			||||||
 | 
					    static const char* vbscript_url = "VBSCRIPT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* covers JAVA, JAVASCRIPT, + colon */
 | 
				
			||||||
 | 
					    static const char* javascript_url = "JAVA";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* skip whitespace */
 | 
				
			||||||
 | 
					    while (len > 0 && (*s <= 32 || *s >= 127)) {
 | 
				
			||||||
 | 
					        /*
 | 
				
			||||||
 | 
					         * HEY: this is a signed character.
 | 
				
			||||||
 | 
					         *  We are intentionally skipping high-bit characters too
 | 
				
			||||||
 | 
					         *  since they are not ASCII, and Opera sometimes uses UTF-8 whitespace.
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * Also in EUC-JP some of the high bytes are just ignored.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        ++s;
 | 
				
			||||||
 | 
					        --len;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (htmlencode_startswith(data_url, s, len)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (htmlencode_startswith(viewsource_url, s, len)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (htmlencode_startswith(javascript_url, s, len)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (htmlencode_startswith(vbscript_url, s, len)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    h5_state_t h5;
 | 
				
			||||||
 | 
					    attribute_t attr = TYPE_NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    libinjection_h5_init(&h5, s, len, (enum html5_flags) flags);
 | 
				
			||||||
 | 
					    while (libinjection_h5_next(&h5)) {
 | 
				
			||||||
 | 
					        if (h5.token_type != ATTR_VALUE) {
 | 
				
			||||||
 | 
					            attr = TYPE_NONE;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (h5.token_type == DOCTYPE) {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					        } else if (h5.token_type == TAG_NAME_OPEN) {
 | 
				
			||||||
 | 
					            if (is_black_tag(h5.token_start, h5.token_len, strictMode)) {
 | 
				
			||||||
 | 
					                return 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else if (h5.token_type == ATTR_NAME) {
 | 
				
			||||||
 | 
					            attr = is_black_attr(h5.token_start, h5.token_len, strictMode);
 | 
				
			||||||
 | 
					        } else if (h5.token_type == ATTR_VALUE) {
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					             * IE6,7,8 parsing works a bit differently so
 | 
				
			||||||
 | 
					             * a whole <script> or other black tag might be hiding
 | 
				
			||||||
 | 
					             * inside an attribute value under HTML 5 parsing
 | 
				
			||||||
 | 
					             * See http://html5sec.org/#102
 | 
				
			||||||
 | 
					             * to avoid doing a full reparse of the value, just
 | 
				
			||||||
 | 
					             * look for "<".  This probably need adjusting to
 | 
				
			||||||
 | 
					             * handle escaped characters
 | 
				
			||||||
 | 
					             */
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					              if (memchr(h5.token_start, '<', h5.token_len) != NULL) {
 | 
				
			||||||
 | 
					              return 1;
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (attr) {
 | 
				
			||||||
 | 
					            case TYPE_NONE:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case TYPE_BLACK:
 | 
				
			||||||
 | 
					                return 1;
 | 
				
			||||||
 | 
					            case TYPE_ATTR_URL:
 | 
				
			||||||
 | 
					                if (is_black_url(h5.token_start, h5.token_len)) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case TYPE_STYLE:
 | 
				
			||||||
 | 
					                return 1;
 | 
				
			||||||
 | 
					            case TYPE_ATTR_INDIRECT:
 | 
				
			||||||
 | 
					                /* an attribute name is specified in a _value_ */
 | 
				
			||||||
 | 
					                if (is_black_attr(h5.token_start, h5.token_len, strictMode)) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					  assert(0);
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            attr = TYPE_NONE;
 | 
				
			||||||
 | 
					        } else if (h5.token_type == TAG_COMMENT) {
 | 
				
			||||||
 | 
					            /* IE uses a "`" as a tag ending char */
 | 
				
			||||||
 | 
					            // goedge: commented for photo uploading
 | 
				
			||||||
 | 
					            /**if (memchr(h5.token_start, '`', h5.token_len) != NULL) {
 | 
				
			||||||
 | 
					                return 1;
 | 
				
			||||||
 | 
					            }**/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* IE conditional comment */
 | 
				
			||||||
 | 
					            if (h5.token_len > 3) {
 | 
				
			||||||
 | 
					                if (h5.token_start[0] == '[' &&
 | 
				
			||||||
 | 
					                    (h5.token_start[1] == 'i' || h5.token_start[1] == 'I') &&
 | 
				
			||||||
 | 
					                    (h5.token_start[2] == 'f' || h5.token_start[2] == 'F')) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if ((h5.token_start[0] == 'x' || h5.token_start[0] == 'X') &&
 | 
				
			||||||
 | 
					                    (h5.token_start[1] == 'm' || h5.token_start[1] == 'M') &&
 | 
				
			||||||
 | 
					                    (h5.token_start[2] == 'l' || h5.token_start[2] == 'L')) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (h5.token_len > 5) {
 | 
				
			||||||
 | 
					                /*  IE <?import pseudo-tag */
 | 
				
			||||||
 | 
					                if (cstrcasecmp_with_null("IMPORT", h5.token_start, 6) == 0) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                /*  XML Entity definition */
 | 
				
			||||||
 | 
					                if (cstrcasecmp_with_null("ENTITY", h5.token_start, 6) == 0) {
 | 
				
			||||||
 | 
					                    return 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * wrapper
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * const char* s: input string, may contain nulls, does not need to be null-terminated.
 | 
				
			||||||
 | 
					 * size_t len: input string length.
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int libinjection_xss(const char* s, size_t slen, int strictMode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (libinjection_is_xss(s, slen, DATA_STATE, strictMode)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (libinjection_is_xss(s, slen, VALUE_NO_QUOTE, strictMode)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (libinjection_is_xss(s, slen, VALUE_SINGLE_QUOTE, strictMode)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (libinjection_is_xss(s, slen, VALUE_DOUBLE_QUOTE, strictMode)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (libinjection_is_xss(s, slen, VALUE_BACK_QUOTE, strictMode)) {
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					#ifndef LIBINJECTION_XSS
 | 
				
			||||||
 | 
					#define LIBINJECTION_XSS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * HEY THIS ISN'T DONE
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* pull in size_t */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int libinjection_is_xss(const char* s, size_t len, int flags, int strictMode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										313
									
								
								internal/waf/injectionutils/libinjection/src/reader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								internal/waf/injectionutils/libinjection/src/reader.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,313 @@
 | 
				
			|||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection.h"
 | 
				
			||||||
 | 
					#include "libinjection_sqli.h"
 | 
				
			||||||
 | 
					#include "libinjection_xss.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef TRUE
 | 
				
			||||||
 | 
					#define TRUE 1
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef FALSE
 | 
				
			||||||
 | 
					#define FALSE 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int g_test_ok = 0;
 | 
				
			||||||
 | 
					static int g_test_fail = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
					    MODE_SQLI,
 | 
				
			||||||
 | 
					    MODE_XSS
 | 
				
			||||||
 | 
					} detect_mode_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void usage(const char* program_name);
 | 
				
			||||||
 | 
					size_t modp_rtrim(char* str, size_t len);
 | 
				
			||||||
 | 
					void modp_toprint(char* str, size_t len);
 | 
				
			||||||
 | 
					void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
 | 
				
			||||||
 | 
					                   int flag_invert, int flag_true, int flag_quiet);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int urlcharmap(char ch);
 | 
				
			||||||
 | 
					size_t modp_url_decode(char* dest, const char* s, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int urlcharmap(char ch) {
 | 
				
			||||||
 | 
					    switch (ch) {
 | 
				
			||||||
 | 
					    case '0': return 0;
 | 
				
			||||||
 | 
					    case '1': return 1;
 | 
				
			||||||
 | 
					    case '2': return 2;
 | 
				
			||||||
 | 
					    case '3': return 3;
 | 
				
			||||||
 | 
					    case '4': return 4;
 | 
				
			||||||
 | 
					    case '5': return 5;
 | 
				
			||||||
 | 
					    case '6': return 6;
 | 
				
			||||||
 | 
					    case '7': return 7;
 | 
				
			||||||
 | 
					    case '8': return 8;
 | 
				
			||||||
 | 
					    case '9': return 9;
 | 
				
			||||||
 | 
					    case 'a': case 'A': return 10;
 | 
				
			||||||
 | 
					    case 'b': case 'B': return 11;
 | 
				
			||||||
 | 
					    case 'c': case 'C': return 12;
 | 
				
			||||||
 | 
					    case 'd': case 'D': return 13;
 | 
				
			||||||
 | 
					    case 'e': case 'E': return 14;
 | 
				
			||||||
 | 
					    case 'f': case 'F': return 15;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        return 256;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					size_t modp_url_decode(char* dest, const char* s, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char* deststart = dest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size_t i = 0;
 | 
				
			||||||
 | 
					    int d = 0;
 | 
				
			||||||
 | 
					    while (i < len) {
 | 
				
			||||||
 | 
					        switch (s[i]) {
 | 
				
			||||||
 | 
					        case '+':
 | 
				
			||||||
 | 
					            *dest++ = ' ';
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case '%':
 | 
				
			||||||
 | 
					            if (i+2 < len) {
 | 
				
			||||||
 | 
					                d = (urlcharmap(s[i+1]) << 4) | urlcharmap(s[i+2]);
 | 
				
			||||||
 | 
					                if ( d < 256) {
 | 
				
			||||||
 | 
					                    *dest = (char) d;
 | 
				
			||||||
 | 
					                    dest++;
 | 
				
			||||||
 | 
					                    i += 3; /* loop will increment one time */
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    *dest++ = '%';
 | 
				
			||||||
 | 
					                    i += 1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                *dest++ = '%';
 | 
				
			||||||
 | 
					                i += 1;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            *dest++ = s[i];
 | 
				
			||||||
 | 
					            i += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    *dest = '\0';
 | 
				
			||||||
 | 
					    return (size_t)(dest - deststart); /* compute "strlen" of dest */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void modp_toprint(char* str, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    size_t i;
 | 
				
			||||||
 | 
					    for (i = 0; i < len; ++i) {
 | 
				
			||||||
 | 
					        if (str[i] < 32 || str[i] > 126) {
 | 
				
			||||||
 | 
					            str[i] = '?';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					size_t modp_rtrim(char* str, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    while (len) {
 | 
				
			||||||
 | 
					        char c = str[len -1];
 | 
				
			||||||
 | 
					        if (c == ' ' || c == '\n' || c == '\t' || c == '\r') {
 | 
				
			||||||
 | 
					            str[len -1] = '\0';
 | 
				
			||||||
 | 
					            len -= 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void test_positive(FILE * fd, const char *fname, detect_mode_t mode,
 | 
				
			||||||
 | 
					                   int flag_invert, int flag_true, int flag_quiet)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    char linebuf[8192];
 | 
				
			||||||
 | 
					    int issqli = 0;
 | 
				
			||||||
 | 
					    int linenum = 0;
 | 
				
			||||||
 | 
					    size_t len;
 | 
				
			||||||
 | 
					    sfilter sf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (fgets(linebuf, sizeof(linebuf), fd)) {
 | 
				
			||||||
 | 
					        linenum += 1;
 | 
				
			||||||
 | 
					        len = modp_rtrim(linebuf, strlen(linebuf));
 | 
				
			||||||
 | 
					        if (len == 0) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (linebuf[0] == '#') {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        len =  modp_url_decode(linebuf, linebuf, len);
 | 
				
			||||||
 | 
					        switch (mode) {
 | 
				
			||||||
 | 
					        case MODE_SQLI: {
 | 
				
			||||||
 | 
					            libinjection_sqli_init(&sf, linebuf, len, 0);
 | 
				
			||||||
 | 
					            issqli = libinjection_is_sqli(&sf);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        case MODE_XSS: {
 | 
				
			||||||
 | 
					            issqli = libinjection_xss(linebuf, len);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            assert(0);
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (issqli) {
 | 
				
			||||||
 | 
					            g_test_ok += 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            g_test_fail += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!flag_quiet) {
 | 
				
			||||||
 | 
					            if ((issqli && flag_true && ! flag_invert) ||
 | 
				
			||||||
 | 
					                (!issqli && flag_true && flag_invert) ||
 | 
				
			||||||
 | 
					                !flag_true) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                modp_toprint(linebuf, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                switch (mode) {
 | 
				
			||||||
 | 
					                case MODE_SQLI: {
 | 
				
			||||||
 | 
							    /*
 | 
				
			||||||
 | 
							     * if we didn't find a SQLi and fingerprint from
 | 
				
			||||||
 | 
					                     * sqlstats is is 'sns' or 'snsns' then redo using
 | 
				
			||||||
 | 
					                     * plain context
 | 
				
			||||||
 | 
							     */
 | 
				
			||||||
 | 
					                    if (!issqli && (strcmp(sf.fingerprint, "sns") == 0 ||
 | 
				
			||||||
 | 
									    strcmp(sf.fingerprint, "snsns") == 0)) {
 | 
				
			||||||
 | 
					                        libinjection_sqli_fingerprint(&sf, 0);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    fprintf(stdout, "%s\t%d\t%s\t%s\t%s\n",
 | 
				
			||||||
 | 
					                            fname, linenum,
 | 
				
			||||||
 | 
					                            (issqli ? "True" : "False"), sf.fingerprint, linebuf);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                case MODE_XSS: {
 | 
				
			||||||
 | 
					                    fprintf(stdout, "%s\t%d\t%s\t%s\n",
 | 
				
			||||||
 | 
					                            fname, linenum,
 | 
				
			||||||
 | 
					                            (issqli ? "True" : "False"), linebuf);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    assert(0);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void usage(const char* program_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  fprintf(stdout, "usage: %s [flags] [files...]\n", program_name);
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-q --quiet     : quiet mode");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-m --max-fails : number of failed cases need to fail entire test");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-s INTEGER     : repeat each test N time "
 | 
				
			||||||
 | 
						  "(for performance testing)");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-t             : only print positive matches");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-x --mode-xss  : test input for XSS");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-i --invert    : invert test logic "
 | 
				
			||||||
 | 
						  "(input is tested for being safe)");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "-? -h -help --help : this page");
 | 
				
			||||||
 | 
					  fprintf(stdout, "%s\n", "");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, const char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * invert output, by
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int flag_invert = FALSE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * don't print anything.. useful for
 | 
				
			||||||
 | 
					     * performance monitors, gprof.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int flag_quiet = FALSE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * only print positive results
 | 
				
			||||||
 | 
					     * with invert, only print negative results
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    int flag_true = FALSE;
 | 
				
			||||||
 | 
					    detect_mode_t mode = MODE_SQLI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int flag_slow = 1;
 | 
				
			||||||
 | 
					    int count = 0;
 | 
				
			||||||
 | 
					    int max = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int i, j;
 | 
				
			||||||
 | 
					    int offset = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (offset < argc) {
 | 
				
			||||||
 | 
					        if (strcmp(argv[offset], "-?") == 0 ||
 | 
				
			||||||
 | 
					            strcmp(argv[offset], "-h") == 0 ||
 | 
				
			||||||
 | 
						    strcmp(argv[offset], "-help") == 0 ||
 | 
				
			||||||
 | 
						    strcmp(argv[offset], "--help") == 0) {
 | 
				
			||||||
 | 
						  usage(argv[0]);
 | 
				
			||||||
 | 
						  exit(0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						  
 | 
				
			||||||
 | 
					        if (strcmp(argv[offset], "-i") == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					            flag_invert = TRUE;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-q") == 0 ||
 | 
				
			||||||
 | 
							   strcmp(argv[offset], "--quiet") == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					            flag_quiet = TRUE;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-t") == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					            flag_true = TRUE;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-s") == 0) {
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					            flag_slow = 100;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-m") == 0 ||
 | 
				
			||||||
 | 
							   strcmp(argv[offset], "--max-fails") == 0) {
 | 
				
			||||||
 | 
							     offset += 1;
 | 
				
			||||||
 | 
					            max = atoi(argv[offset]);
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-x") == 0 ||
 | 
				
			||||||
 | 
							   strcmp(argv[offset], "--mode-xss") == 0) {
 | 
				
			||||||
 | 
					            mode = MODE_XSS;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (offset == argc) {
 | 
				
			||||||
 | 
					        test_positive(stdin, "stdin", mode, flag_invert, flag_true, flag_quiet);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        for (j = 0; j < flag_slow; ++j) {
 | 
				
			||||||
 | 
					            for (i = offset; i < argc; ++i) {
 | 
				
			||||||
 | 
					                FILE* fd = fopen(argv[i], "r");
 | 
				
			||||||
 | 
					                if (fd) {
 | 
				
			||||||
 | 
					                    test_positive(fd, argv[i], mode, flag_invert, flag_true, flag_quiet);
 | 
				
			||||||
 | 
					                    fclose(fd);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!flag_quiet) {
 | 
				
			||||||
 | 
					        fprintf(stdout, "%s", "\n");
 | 
				
			||||||
 | 
					        fprintf(stdout, "SQLI  : %d\n", g_test_ok);
 | 
				
			||||||
 | 
					        fprintf(stdout, "SAFE  : %d\n", g_test_fail);
 | 
				
			||||||
 | 
					        fprintf(stdout, "TOTAL : %d\n", g_test_ok + g_test_fail);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (max == -1) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    count = g_test_ok;
 | 
				
			||||||
 | 
					    if (flag_invert) {
 | 
				
			||||||
 | 
					        count = g_test_fail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (count > max) {
 | 
				
			||||||
 | 
					        printf("\nThreshold is %d, got %d, failing.\n", max, count);
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        printf("\nThreshold is %d, got %d, passing.\n", max, count);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										165
									
								
								internal/waf/injectionutils/libinjection/src/sqli_cli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								internal/waf/injectionutils/libinjection/src/sqli_cli.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
				
			|||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Copyright 2012, 2013 Nick Galbreath
 | 
				
			||||||
 | 
					 * nickg@client9.com
 | 
				
			||||||
 | 
					 * BSD License -- see COPYING.txt for details
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is for testing against files in ../data/ *.txt
 | 
				
			||||||
 | 
					 * Reads from stdin or a list of files, and emits if a line
 | 
				
			||||||
 | 
					 * is a SQLi attack or not, and does basic statistics
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection.h"
 | 
				
			||||||
 | 
					#include "libinjection_sqli.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_string(stoken_t* t);
 | 
				
			||||||
 | 
					void print_var(stoken_t* t);
 | 
				
			||||||
 | 
					void print_token(stoken_t *t);
 | 
				
			||||||
 | 
					void usage(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_string(stoken_t* t)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* print opening quote */
 | 
				
			||||||
 | 
					    if (t->str_open != '\0') {
 | 
				
			||||||
 | 
					        printf("%c", t->str_open);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* print content */
 | 
				
			||||||
 | 
					    printf("%s", t->val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* print closing quote */
 | 
				
			||||||
 | 
					    if (t->str_close != '\0') {
 | 
				
			||||||
 | 
					        printf("%c", t->str_close);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_var(stoken_t* t)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (t->count >= 1) {
 | 
				
			||||||
 | 
					        printf("%c", '@');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (t->count == 2) {
 | 
				
			||||||
 | 
					        printf("%c", '@');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    print_string(t);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void print_token(stoken_t *t) {
 | 
				
			||||||
 | 
					    printf("%c ", t->type);
 | 
				
			||||||
 | 
					    switch (t->type) {
 | 
				
			||||||
 | 
					    case 's':
 | 
				
			||||||
 | 
					        print_string(t);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    case 'v':
 | 
				
			||||||
 | 
					        print_var(t);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					        printf("%s", t->val);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    printf("%s", "\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void usage(void) {
 | 
				
			||||||
 | 
					    printf("\n");
 | 
				
			||||||
 | 
					    printf("libinjection sqli tester\n");
 | 
				
			||||||
 | 
					    printf("\n");
 | 
				
			||||||
 | 
					    printf(" -ca  parse as ANSI SQL\n");
 | 
				
			||||||
 | 
					    printf(" -cm  parse as MySQL SQL\n");
 | 
				
			||||||
 | 
					    printf(" -q0  parse as is\n");
 | 
				
			||||||
 | 
					    printf(" -q1  parse in single-quote mode\n");
 | 
				
			||||||
 | 
					    printf(" -q2  parse in doiuble-quote mode\n");
 | 
				
			||||||
 | 
					    printf("\n");
 | 
				
			||||||
 | 
					    printf(" -f --fold  fold results\n");
 | 
				
			||||||
 | 
					    printf("\n");
 | 
				
			||||||
 | 
					    printf(" -d --detect  detect SQLI.  empty reply = not detected\n");
 | 
				
			||||||
 | 
					    printf("\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, const char* argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    size_t slen;
 | 
				
			||||||
 | 
					    char* copy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int flags = 0;
 | 
				
			||||||
 | 
					    int fold = 0;
 | 
				
			||||||
 | 
					    int detect = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					    int count;
 | 
				
			||||||
 | 
					    int offset = 1;
 | 
				
			||||||
 | 
					    int issqli;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sfilter sf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (argc < 2) {
 | 
				
			||||||
 | 
					        usage();
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
						if (strcmp(argv[offset], "-h") == 0 || strcmp(argv[offset], "-?") == 0 || strcmp(argv[offset], "--help") == 0) {
 | 
				
			||||||
 | 
						    usage();
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					        if (strcmp(argv[offset], "-m") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_SQL_MYSQL;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (strcmp(argv[offset], "-f") == 0 || strcmp(argv[offset], "--fold") == 0) {
 | 
				
			||||||
 | 
					            fold = 1;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-d") == 0 || strcmp(argv[offset], "--detect") == 0) {
 | 
				
			||||||
 | 
					            detect = 1;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-ca") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_SQL_ANSI;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-cm") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_SQL_MYSQL;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-q0") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_QUOTE_NONE;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-q1") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_QUOTE_SINGLE;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else if (strcmp(argv[offset], "-q2") == 0) {
 | 
				
			||||||
 | 
					            flags |= FLAG_QUOTE_DOUBLE;
 | 
				
			||||||
 | 
					            offset += 1;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ATTENTION: argv is a C-string, null terminated.  We copy this
 | 
				
			||||||
 | 
					     * to it's own location, WITHOUT null byte.  This way, valgrind
 | 
				
			||||||
 | 
					     * can see if we run past the buffer.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    slen = strlen(argv[offset]);
 | 
				
			||||||
 | 
					    copy = (char* ) malloc(slen);
 | 
				
			||||||
 | 
					    memcpy(copy, argv[offset], slen);
 | 
				
			||||||
 | 
					    libinjection_sqli_init(&sf, copy, slen, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (detect == 1) {
 | 
				
			||||||
 | 
					        issqli = libinjection_is_sqli(&sf);
 | 
				
			||||||
 | 
					        if (issqli) {
 | 
				
			||||||
 | 
					            printf("%s\n", sf.fingerprint);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else if (fold == 1) {
 | 
				
			||||||
 | 
					        count = libinjection_sqli_fold(&sf);
 | 
				
			||||||
 | 
					        for (i = 0; i < count; ++i) {
 | 
				
			||||||
 | 
					            print_token(&(sf.tokenvec[i]));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        while (libinjection_sqli_tokenize(&sf)) {
 | 
				
			||||||
 | 
					            print_token(sf.current);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    free(copy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										132
									
								
								internal/waf/injectionutils/libinjection/src/sqlparse2c.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										132
									
								
								internal/waf/injectionutils/libinjection/src/sqlparse2c.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					#  Copyright 2012, 2013 Nick Galbreath
 | 
				
			||||||
 | 
					#  nickg@client9.com
 | 
				
			||||||
 | 
					#  BSD License -- see COPYING.txt for details
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Converts a libinjection JSON data file to a C header (.h) file
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def toc(obj):
 | 
				
			||||||
 | 
					    """ main routine """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("""
 | 
				
			||||||
 | 
					#ifndef LIBINJECTION_SQLI_DATA_H
 | 
				
			||||||
 | 
					#define LIBINJECTION_SQLI_DATA_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection.h"
 | 
				
			||||||
 | 
					#include "libinjection_sqli.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
					    const char *word;
 | 
				
			||||||
 | 
					    char type;
 | 
				
			||||||
 | 
					} keyword_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t parse_money(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_other(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_white(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_operator1(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_char(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_hash(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_dash(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_slash(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_backslash(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_operator2(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_string(sfilter *sf);
 | 
				
			||||||
 | 
					static size_t parse_word(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_var(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_number(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_tick(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_ustring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_qstring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_nqstring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_xstring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_bstring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_estring(sfilter * sf);
 | 
				
			||||||
 | 
					static size_t parse_bword(sfilter * sf);
 | 
				
			||||||
 | 
					""")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    # Mapping of character to function
 | 
				
			||||||
 | 
					    #
 | 
				
			||||||
 | 
					    fnmap = {
 | 
				
			||||||
 | 
					        'CHAR_WORD' : 'parse_word',
 | 
				
			||||||
 | 
					        'CHAR_WHITE': 'parse_white',
 | 
				
			||||||
 | 
					        'CHAR_OP1'  : 'parse_operator1',
 | 
				
			||||||
 | 
						'CHAR_UNARY': 'parse_operator1',
 | 
				
			||||||
 | 
					        'CHAR_OP2'  : 'parse_operator2',
 | 
				
			||||||
 | 
						'CHAR_BANG' : 'parse_operator2',
 | 
				
			||||||
 | 
					        'CHAR_BACK' : 'parse_backslash',
 | 
				
			||||||
 | 
					        'CHAR_DASH' : 'parse_dash',
 | 
				
			||||||
 | 
					        'CHAR_STR'  : 'parse_string',
 | 
				
			||||||
 | 
					        'CHAR_HASH' : 'parse_hash',
 | 
				
			||||||
 | 
					        'CHAR_NUM'  : 'parse_number',
 | 
				
			||||||
 | 
					        'CHAR_SLASH': 'parse_slash',
 | 
				
			||||||
 | 
					        'CHAR_SEMICOLON' : 'parse_char',
 | 
				
			||||||
 | 
						'CHAR_COMMA': 'parse_char',
 | 
				
			||||||
 | 
						'CHAR_LEFTPARENS': 'parse_char',
 | 
				
			||||||
 | 
						'CHAR_RIGHTPARENS': 'parse_char',
 | 
				
			||||||
 | 
						'CHAR_LEFTBRACE': 'parse_char',
 | 
				
			||||||
 | 
						'CHAR_RIGHTBRACE': 'parse_char',
 | 
				
			||||||
 | 
					        'CHAR_VAR'  : 'parse_var',
 | 
				
			||||||
 | 
					        'CHAR_OTHER': 'parse_other',
 | 
				
			||||||
 | 
					        'CHAR_MONEY': 'parse_money',
 | 
				
			||||||
 | 
					        'CHAR_TICK' : 'parse_tick',
 | 
				
			||||||
 | 
					        'CHAR_UNDERSCORE': 'parse_underscore',
 | 
				
			||||||
 | 
					        'CHAR_USTRING'   : 'parse_ustring',
 | 
				
			||||||
 | 
					        'CHAR_QSTRING'   : 'parse_qstring',
 | 
				
			||||||
 | 
					        'CHAR_NQSTRING'  : 'parse_nqstring',
 | 
				
			||||||
 | 
					        'CHAR_XSTRING'   : 'parse_xstring',
 | 
				
			||||||
 | 
					        'CHAR_BSTRING'   : 'parse_bstring',
 | 
				
			||||||
 | 
					        'CHAR_ESTRING'   : 'parse_estring',
 | 
				
			||||||
 | 
					        'CHAR_BWORD'     : 'parse_bword'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    print()
 | 
				
			||||||
 | 
					    print("typedef size_t (*pt2Function)(sfilter *sf);")
 | 
				
			||||||
 | 
					    print("static const pt2Function char_parse_map[] = {")
 | 
				
			||||||
 | 
					    pos = 0
 | 
				
			||||||
 | 
					    for character in obj['charmap']:
 | 
				
			||||||
 | 
					        print("   &%s, /* %d */" % (fnmap[character], pos))
 | 
				
			||||||
 | 
					        pos += 1
 | 
				
			||||||
 | 
					    print("};")
 | 
				
			||||||
 | 
					    print()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # keywords
 | 
				
			||||||
 | 
					    #  load them
 | 
				
			||||||
 | 
					    keywords = obj['keywords']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for  fingerprint in list(obj['fingerprints']):
 | 
				
			||||||
 | 
					        fingerprint = '0' + fingerprint.upper()
 | 
				
			||||||
 | 
					        keywords[fingerprint] = 'F'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    needhelp = []
 | 
				
			||||||
 | 
					    for key  in keywords.keys():
 | 
				
			||||||
 | 
					        if key != key.upper():
 | 
				
			||||||
 | 
					            needhelp.append(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for key in needhelp:
 | 
				
			||||||
 | 
					        tmpv = keywords[key]
 | 
				
			||||||
 | 
					        del keywords[key]
 | 
				
			||||||
 | 
					        keywords[key.upper()] = tmpv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("static const keyword_t sql_keywords[] = {")
 | 
				
			||||||
 | 
					    for k in sorted(keywords.keys()):
 | 
				
			||||||
 | 
					        if len(k) > 31:
 | 
				
			||||||
 | 
					            sys.stderr.write("ERROR: keyword greater than 32 chars\n")
 | 
				
			||||||
 | 
					            sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print("    {\"%s\", '%s'}," % (k, keywords[k]))
 | 
				
			||||||
 | 
					    print("};")
 | 
				
			||||||
 | 
					    print("static const size_t sql_keywords_sz = %d;" % (len(keywords), ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("#endif")
 | 
				
			||||||
 | 
					    return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    import json
 | 
				
			||||||
 | 
					    sys.exit(toc(json.load(sys.stdin)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								internal/waf/injectionutils/libinjection_sqli.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								internal/waf/injectionutils/libinjection_sqli.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					#define LIBINJECTION_VERSION "3.9.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection/src/libinjection_sqli.c"
 | 
				
			||||||
							
								
								
									
										6
									
								
								internal/waf/injectionutils/libinjection_xss.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								internal/waf/injectionutils/libinjection_xss.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					#define LIBINJECTION_VERSION "3.9.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "libinjection/src/libinjection_xss.c"
 | 
				
			||||||
 | 
					#include "libinjection/src/libinjection_html5.c"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GOEDGE_VERSION "25" // last version is for GoEdge change
 | 
				
			||||||
							
								
								
									
										90
									
								
								internal/waf/injectionutils/utils_sqli.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								internal/waf/injectionutils/utils_sqli.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package injectionutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					#cgo CFLAGS: -O2 -I./libinjection/src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libinjection.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode/utf8"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DetectSQLInjectionCache detect sql injection in string with cache
 | 
				
			||||||
 | 
					func DetectSQLInjectionCache(input string, isStrict bool, cacheLife int) bool {
 | 
				
			||||||
 | 
						var l = len(input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if l == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cacheLife <= 0 || l < 128 || l > MaxCacheDataSize {
 | 
				
			||||||
 | 
							return DetectSQLInjection(input, isStrict)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result = DetectSQLInjection(input, isStrict)
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DetectSQLInjection detect sql injection in string
 | 
				
			||||||
 | 
					func DetectSQLInjection(input string, isStrict bool) bool {
 | 
				
			||||||
 | 
						if len(input) == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !isStrict {
 | 
				
			||||||
 | 
							if len(input) > 1024 {
 | 
				
			||||||
 | 
								if !utf8.ValidString(input[:1024]) && !utf8.ValidString(input[:1023]) && !utf8.ValidString(input[:1022]) {
 | 
				
			||||||
 | 
									return false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if !utf8.ValidString(input) {
 | 
				
			||||||
 | 
									return false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if detectSQLInjectionOne(input) {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 兼容 /PATH?URI
 | 
				
			||||||
 | 
						if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
 | 
				
			||||||
 | 
							var argsIndex = strings.Index(input, "?")
 | 
				
			||||||
 | 
							if argsIndex > 0 {
 | 
				
			||||||
 | 
								var args = input[argsIndex+1:]
 | 
				
			||||||
 | 
								unescapeArgs, err := url.QueryUnescape(args)
 | 
				
			||||||
 | 
								if err == nil && args != unescapeArgs {
 | 
				
			||||||
 | 
									return detectSQLInjectionOne(args) || detectSQLInjectionOne(unescapeArgs)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return detectSQLInjectionOne(args)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							unescapedInput, err := url.QueryUnescape(input)
 | 
				
			||||||
 | 
							if err == nil && input != unescapedInput {
 | 
				
			||||||
 | 
								return detectSQLInjectionOne(unescapedInput)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func detectSQLInjectionOne(input string) bool {
 | 
				
			||||||
 | 
						if len(input) == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var fingerprint [8]C.char
 | 
				
			||||||
 | 
						var fingerprintPtr = (*C.char)(unsafe.Pointer(&fingerprint[0]))
 | 
				
			||||||
 | 
						var cInput = C.CString(input)
 | 
				
			||||||
 | 
						defer C.free(unsafe.Pointer(cInput))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return C.libinjection_sqli(cInput, C.size_t(len(input)), fingerprintPtr) == 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										129
									
								
								internal/waf/injectionutils/utils_sqli_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								internal/waf/injectionutils/utils_sqli_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package injectionutils_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/assert"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/rands"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/types"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDetectSQLInjection(t *testing.T) {
 | 
				
			||||||
 | 
						var a = assert.NewAssertion(t)
 | 
				
			||||||
 | 
						for _, isStrict := range []bool{true, false} {
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("' UNION SELECT * FROM myTable", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("id=1 ' UNION  select * from a", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", isStrict))
 | 
				
			||||||
 | 
							a.IsFalse(injectionutils.DetectSQLInjection("' UNION SELECT1 * FROM myTable", isStrict))
 | 
				
			||||||
 | 
							a.IsFalse(injectionutils.DetectSQLInjection("1234", isStrict))
 | 
				
			||||||
 | 
							a.IsFalse(injectionutils.DetectSQLInjection("", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("id=123 OR 1=1&b=2", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("id=123&b=456&c=1' or 2=2", isStrict))
 | 
				
			||||||
 | 
							a.IsFalse(injectionutils.DetectSQLInjection("?", isStrict))
 | 
				
			||||||
 | 
							a.IsFalse(injectionutils.DetectSQLInjection("/hello?age=22", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/sql/injection?id=123%20or%201=1", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("id=123%20or%201=1", isStrict))
 | 
				
			||||||
 | 
							a.IsTrue(injectionutils.DetectSQLInjection("https://example.com/' or 1=1", isStrict))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("asdf asd ; -1' and 1=1 union/* foo */select load_file('/etc/passwd')--", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_URL(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("/sql/injection?id=123 or 1=1", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_Normal_Small(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("a/sql/injection?id=1234", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_URL_Normal_Small(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("/sql/injection?id="+types.String(rands.Int64()%10000), false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_URL_Normal_Middle(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("/search?q=libinjection+fingerprint&newwindow=1&sca_esv=589290862&sxsrf=AMwHvKnxuLoejn2XlNniffC12E_xc35M7Q%3A1702090118361&ei=htvzzebfFZfo1e8PvLGggAk&ved=0ahUKEwjTsYmnq4GDAxUWdPOHHbwkCJAQ4ddDCBA&uact=5&oq=libinjection+fingerprint&gs_lp=Egxnd3Mtd2l6LXNlcnAiGIxpYmluamVjdGlvbmBmaW5nKXJwcmludTIEEAAYHjIGVAAYCBgeSiEaUPkRWKFZcAJ4AZABAJgBHgGgAfoEqgwDMC40uAEGyAEA-AEBwgIKEAFYTxjWMuiwA-IDBBgAVteIBgGQBgI&sclient=gws-wiz-serp#ip=1", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_URL_Normal_Small_Cache(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjectionCache("/sql/injection?id="+types.String(rands.Int64()%10000), false, 1800)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_Normal_Large(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var s = strings.Repeat("A", 512)
 | 
				
			||||||
 | 
						b.ResetTimer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s+"&v=%20", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_Normal_Large_Cache(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var s = strings.Repeat("A", 512)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjectionCache("a/sql/injection?id="+types.String(rands.Int64()%10000)+"&s="+s, false, 1800)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectSQLInjection_URL_Unescape(b *testing.B) {
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectSQLInjection("/sql/injection?id=123%20or%201=1", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								internal/waf/injectionutils/utils_xss.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								internal/waf/injectionutils/utils_xss.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package injectionutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					#cgo CFLAGS: -O2 -I./libinjection/src
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libinjection.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					import "C"
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/cespare/xxhash/v2"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unsafe"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MaxCacheDataSize = 1 << 20
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func DetectXSSCache(input string, isStrict bool, cacheLife int) bool {
 | 
				
			||||||
 | 
						var l = len(input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if l == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cacheLife <= 0 || l < 512 || l > MaxCacheDataSize {
 | 
				
			||||||
 | 
							return DetectXSS(input, isStrict)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var hash = xxhash.Sum64String(input)
 | 
				
			||||||
 | 
						var key = "WAF@XSS@" + strconv.FormatUint(hash, 10)
 | 
				
			||||||
 | 
						if isStrict {
 | 
				
			||||||
 | 
							key += "@1"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var result = DetectXSS(input, isStrict)
 | 
				
			||||||
 | 
						return result
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// DetectXSS detect XSS in string
 | 
				
			||||||
 | 
					func DetectXSS(input string, isStrict bool) bool {
 | 
				
			||||||
 | 
						if len(input) == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if detectXSSOne(input, isStrict) {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 兼容 /PATH?URI
 | 
				
			||||||
 | 
						if (input[0] == '/' || strings.HasPrefix(input, "http://") || strings.HasPrefix(input, "https://")) && len(input) < 1024 {
 | 
				
			||||||
 | 
							var argsIndex = strings.Index(input, "?")
 | 
				
			||||||
 | 
							if argsIndex > 0 {
 | 
				
			||||||
 | 
								var args = input[argsIndex+1:]
 | 
				
			||||||
 | 
								unescapeArgs, err := url.QueryUnescape(args)
 | 
				
			||||||
 | 
								if err == nil && args != unescapeArgs {
 | 
				
			||||||
 | 
									return detectXSSOne(args, isStrict) || detectXSSOne(unescapeArgs, isStrict)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return detectXSSOne(args, isStrict)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							unescapedInput, err := url.QueryUnescape(input)
 | 
				
			||||||
 | 
							if err == nil && input != unescapedInput {
 | 
				
			||||||
 | 
								return detectXSSOne(unescapedInput, isStrict)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func detectXSSOne(input string, isStrict bool) bool {
 | 
				
			||||||
 | 
						if len(input) == 0 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var cInput = C.CString(input)
 | 
				
			||||||
 | 
						defer C.free(unsafe.Pointer(cInput))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var isStrictInt = 0
 | 
				
			||||||
 | 
						if isStrict {
 | 
				
			||||||
 | 
							isStrictInt = 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return C.libinjection_xss(cInput, C.size_t(len(input)), C.int(isStrictInt)) == 1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										93
									
								
								internal/waf/injectionutils/utils_xss_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								internal/waf/injectionutils/utils_xss_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package injectionutils_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/assert"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDetectXSS(t *testing.T) {
 | 
				
			||||||
 | 
						var a = assert.NewAssertion(t)
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("", true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("abc", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("<script>", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("<link>", true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("<html><span>", true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("<script>", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("/path?onmousedown=a", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("/path?onkeyup=a", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("onkeyup=a", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("<iframe scrolling='no'>", true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", true))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("name=s&description=%3Cscript+src%3D%22a.js%22%3Edddd%3C%2Fscript%3E", true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0">
 | 
				
			||||||
 | 
					   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
 | 
				
			||||||
 | 
					      <rdf:Description rdf:about=""
 | 
				
			||||||
 | 
					            xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
 | 
				
			||||||
 | 
					         <tiff:Orientation>1</tiff:Orientation>
 | 
				
			||||||
 | 
					      </rdf:Description>
 | 
				
			||||||
 | 
					   </rdf:RDF>
 | 
				
			||||||
 | 
					</x:xmpmeta>`, true)) // included in some photo files
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDetectXSS_Strict(t *testing.T) {
 | 
				
			||||||
 | 
						var a = assert.NewAssertion(t)
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<xml></xml>`, false))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS(`<xml></xml>`, true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<img src=\"\"/>`, false))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<img src=\"test.jpg\"/>`, true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<a href="aaaa"></a>`, true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS(`<span style="color: red"></span>`, false))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS(`<span style="color: red"></span>`, true))
 | 
				
			||||||
 | 
						a.IsFalse(injectionutils.DetectXSS("https://example.com?style=list", false))
 | 
				
			||||||
 | 
						a.IsTrue(injectionutils.DetectXSS("https://example.com?style=list", true))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectXSS_MISS(b *testing.B) {
 | 
				
			||||||
 | 
						var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
				
			||||||
 | 
						if result {
 | 
				
			||||||
 | 
							b.Fatal("'result' should not be 'true'")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectXSS_MISS_Cache(b *testing.B) {
 | 
				
			||||||
 | 
						var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span></body></html>", false)
 | 
				
			||||||
 | 
						if result {
 | 
				
			||||||
 | 
							b.Fatal("'result' should not be 'true'")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectXSSCache("<html><body><span>RequestId: 1234567890</span></body></html>", false, 1800)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkDetectXSS_HIT(b *testing.B) {
 | 
				
			||||||
 | 
						var result = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
 | 
				
			||||||
 | 
						if !result {
 | 
				
			||||||
 | 
							b.Fatal("'result' should not be 'false'")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runtime.GOMAXPROCS(4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.RunParallel(func(pb *testing.PB) {
 | 
				
			||||||
 | 
							for pb.Next() {
 | 
				
			||||||
 | 
								_ = injectionutils.DetectXSS("<html><body><span>RequestId: 1234567890</span><script src=\"\"></script></body></html>", false)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,6 +7,7 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/goman"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/goman"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/setup"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/setup"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/langs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/langs"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
						"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
				
			||||||
@@ -115,6 +116,13 @@ func (this *userMustAuth) BeforeAction(actionPtr actions.ActionWrapper, paramNam
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检测注入
 | 
				
			||||||
 | 
						if injectionutils.DetectXSS(action.Request.RequestURI, false) || injectionutils.DetectSQLInjection(action.Request.RequestURI, false) {
 | 
				
			||||||
 | 
							action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
				
			||||||
 | 
							_, _ = action.ResponseWriter.Write([]byte("Denied By WAF"))
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 恢复模式
 | 
						// 恢复模式
 | 
				
			||||||
	if teaconst.IsRecoverMode {
 | 
						if teaconst.IsRecoverMode {
 | 
				
			||||||
		action.RedirectURL("/recover")
 | 
							action.RedirectURL("/recover")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import (
 | 
				
			|||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
 | 
				
			||||||
	teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
						teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
 | 
				
			||||||
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/waf/injectionutils"
 | 
				
			||||||
	"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
						"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/index/loginutils"
 | 
				
			||||||
	"github.com/iwind/TeaGo/actions"
 | 
						"github.com/iwind/TeaGo/actions"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
@@ -27,6 +28,13 @@ func (this *UserShouldAuth) BeforeAction(actionPtr actions.ActionWrapper, paramN
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检测注入
 | 
				
			||||||
 | 
						if injectionutils.DetectXSS(this.action.Request.RequestURI, false) || injectionutils.DetectSQLInjection(this.action.Request.RequestURI, false) {
 | 
				
			||||||
 | 
							this.action.ResponseWriter.WriteHeader(http.StatusForbidden)
 | 
				
			||||||
 | 
							_, _ = this.action.ResponseWriter.Write([]byte("Denied By WAF"))
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 安全相关
 | 
						// 安全相关
 | 
				
			||||||
	var action = this.action
 | 
						var action = this.action
 | 
				
			||||||
	securityConfig, _ := configloaders.LoadSecurityConfig()
 | 
						securityConfig, _ := configloaders.LoadSecurityConfig()
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user