static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_loc_conf_t *xlcf; ngx_str_t callback; u_char *p, *src, *dst; if (r != r->main) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped in subrequests"); return ngx_http_next_header_filter(r); } xlcf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if (!xlcf->get_enabled) { return ngx_http_next_header_filter(r); } if (r->method != NGX_HTTP_GET) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to the unmatched request method: %V", &r->method_name); return ngx_http_next_header_filter(r); } if (xlcf->check_status) { if (r->headers_out.status != NGX_HTTP_OK && r->headers_out.status != NGX_HTTP_CREATED) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched response status " "\"%ui\"", r->headers_out.status); return ngx_http_next_header_filter(r); } } if (xlcf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg " "specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &xlcf->input_types) == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped due to unmatched Content-Type response " "header"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, xlcf->callback_arg.data, xlcf->callback_arg.len, &callback) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss skipped: no GET argument \"%V\" specified in " "the request", &xlcf->callback_arg); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback(callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * ctx->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = xlcf->output_type; r->headers_out.content_type_len = xlcf->output_type.len; r->headers_out.content_type_lowcase = NULL; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "xss output Content-Type header \"%V\"", &xlcf->output_type); ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); if (xlcf->override_status && r->headers_out.status >= NGX_HTTP_SPECIAL_RESPONSE) { r->headers_out.status = NGX_HTTP_OK; } return ngx_http_next_header_filter(r); }
static ngx_int_t ngx_http_xss_header_filter(ngx_http_request_t *r) { ngx_http_xss_ctx_t *ctx; ngx_http_xss_conf_t *conf; ngx_str_t callback; u_char *p, *src, *dst; if (r->headers_out.status != NGX_HTTP_OK || r != r->main) { dd("skipped: status not 200 or in subrequest"); return ngx_http_next_header_filter(r); } conf = ngx_http_get_module_loc_conf(r, ngx_http_xss_filter_module); if ( ! conf->get_enabled || r->method != NGX_HTTP_GET) { dd("skipped: get_enabled disabled or the current method is not GET"); return ngx_http_next_header_filter(r); } if (conf->callback_arg.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: xss_get is enabled but no xss_callback_arg specified"); return ngx_http_next_header_filter(r); } if (ngx_http_test_content_type(r, &conf->input_types) == NULL) { dd("skipped: content type test not passed"); return ngx_http_next_header_filter(r); } if (ngx_http_arg(r, conf->callback_arg.data, conf->callback_arg.len, &callback) != NGX_OK) { dd("skipped: no callback arg found in the current request: %.*s", conf->callback_arg.len, conf->callback_arg.data); return ngx_http_next_header_filter(r); } p = ngx_palloc(r->pool, callback.len); if (p == NULL) { return NGX_ERROR; } src = callback.data; dst = p; ngx_unescape_uri(&dst, &src, callback.len, NGX_UNESCAPE_URI_COMPONENT); if (src != callback.data + callback.len) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: unescape uri: input data not consumed completely"); return NGX_ERROR; } callback.data = p; callback.len = dst - p; if (ngx_http_xss_test_callback((char *) callback.data, callback.len) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "xss: bad callback argument: \"%V\"", &callback); return ngx_http_next_header_filter(r); } ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xss_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } /* * set by ngx_pcalloc(): * * ctx->callback = { 0, NULL }; * conf->before_body_sent = 0; */ ctx->callback = callback; ngx_http_set_ctx(r, ctx, ngx_http_xss_filter_module); r->headers_out.content_type = conf->output_type; r->headers_out.content_type_len = conf->output_type.len; ngx_http_clear_content_length(r); ngx_http_clear_accept_ranges(r); return ngx_http_next_header_filter(r); }