Compare commits

...

60 Commits

Author SHA1 Message Date
31fa31bd53 Updated changelog 2018-10-29 13:22:01 +01:00
a8a3ca5b97 typo. 2018-10-29 13:15:32 +01:00
7c6fe5a04a Added HTTP_COOKIE.set_expiration_from_max_age, to add the "Expiration:" based on the max-age value. 2018-10-29 13:15:14 +01:00
c8e2009638 Use double quotes only when needed for put_content_type_with_parameters. 2018-10-29 13:11:19 +01:00
627ec7aefc removed unneeded inheritance. 2018-10-29 13:04:46 +01:00
e7087bcbc1 Added missing WSF_TIMEOUT_UTILITIES file. 2018-10-29 12:19:36 +01:00
8d881bcd7d Updated changelog.md 2018-10-29 12:16:01 +01:00
ed3ad962d1 Updated a few classes from http_client to use nanoseconds as timeout precision.
Fixed typo in comments.
2018-10-29 12:15:20 +01:00
d3e865cf6c Fixed setting of socket.timeout in httpd (was not currently set before).
Adopted the nanoseconds timeout precision
 - in config file added support for ns, us, ms, s timeout precision (without indication, it uses `seconds` precision).
2018-10-29 11:27:26 +01:00
9fcd30b4e1 removed useless JWT_ENCODER 2018-10-17 14:25:56 +02:00
0baa05cf63 JWT: updated to make JWT algorithm support more flexible, and simple to extend with specific algorithm. 2018-10-17 11:00:20 +02:00
f97f59b703 renamed a png file to avoid blank character. 2018-09-27 22:01:34 +02:00
dc377b84d3 Duplicated images to see expected images embedded in github markdown pages, and also in github web pages (jekyll on eiffelweb.org). 2018-09-27 21:57:16 +02:00
f14431fc05 Update basics.md 2018-09-27 21:50:02 +02:00
9577d7d82a Update basics.md 2018-09-27 21:48:45 +02:00
99f8377721 Include images twices to see them under github pages, and also in markdown pages 2018-09-27 21:47:48 +02:00
73d5555532 test (ignore) 2018-09-27 21:39:58 +02:00
ce3c7ac57a try change for related links. 2018-09-27 21:34:18 +02:00
Javier Velilla
8754c2d67d Update basics.md 2018-09-27 16:22:20 -03:00
5e928b9a47 use image location that works for md files, and also generated web files (jekyll) 2018-09-27 21:10:04 +02:00
Javier Velilla
9cdfbd2538 Update basics.md 2018-09-27 16:08:39 -03:00
e4fcc863ca Updated image locations 2018-09-27 21:05:54 +02:00
Javier Velilla
7c8d6b9eef Updated path to Application execution png file 2018-09-27 15:58:09 -03:00
a97eb4b062 Added missing dependencies. 2018-05-30 19:27:04 +02:00
bd5aba3db6 Updated Windows DOS script to build the libfcgi binary lib files. 2018-05-30 17:30:05 +02:00
d43c4edb7d Updated the default rescue response (i.e when exception or bad internal error occurs).
Factorized the implementation in WGI_RESCUE_EXECUTION, and now by redefining the `WGI_EXECUTION.execute_rescue (...)` procedure, it is possible to have a custom response on such rescued execution.
2018-05-30 17:28:24 +02:00
9cdd676417 Fixed HTTP_HEADER.put_raw_header (..) by ignoring any empty line of the argument value.
(note: "%R" is considered as empty line here.)
2018-05-30 17:25:04 +02:00
cb273c3176 Updated to compile with upcoming EiffelStudio 18.05 (with and without ssl). 2018-05-28 17:21:11 +02:00
ec7d504502 Fixed EOL. 2018-05-28 16:18:59 +02:00
7ed1e815b0 Updated to compile with upcoming EiffelStudio 18.05 . 2018-05-28 16:11:24 +02:00
da2e26f697 Renamed the fcgi executable. 2018-04-26 11:58:00 +02:00
bc169d6b26 Fixed remaining issues with docker setup. 2018-04-26 10:58:01 +02:00
cf2f0f09fa Updated container files. 2018-04-24 15:13:47 +02:00
207a109e44 Updated to match docker expectation. 2018-04-24 10:36:20 +02:00
2f2e2067ba Added an example to run the debug app with apache2+libfcgi inside a docker container. 2018-04-23 22:13:07 +02:00
7aa7bf1ab2 Updated travis CI config.
Updated install dos script to include wsf_security.
2018-02-13 18:49:14 +01:00
8e8c3602c6 Allow (websocket) upgrade even without persistent connection for normal http request.
(note: this allows to use websocket in single-threaded mode, and avoid the keep-alive-timeout delay before websocket begins its execution)
2018-02-13 18:39:47 +01:00
Jocelyn Fiat
ffd7dd8540 Improved WSF download response implementation.
- Do not set Transfer-Encoding to binary for download response.
- Use WSF_RESPONSE.put_file_content (...)
2018-02-05 21:02:23 +01:00
Jocelyn Fiat
947c94644e Apply win32 workaround only on Windows 32bits. 2018-02-02 22:43:50 +01:00
Jocelyn Fiat
6a779797a5 Removed a few obsolete calls.
Updated tests/all.ecf to include a few missing libraries.
2018-02-02 22:40:18 +01:00
Jocelyn Fiat
7b0ccc0aec Merge branch 'develop' 2018-01-29 22:16:57 +01:00
Jocelyn Fiat
74001fe674 Properly JSON encode null character as \u0000 . 2018-01-29 22:13:03 +01:00
Jocelyn Fiat
c7eb12ad8e Return NotFound response for "/favicon.ico" request instead of returning wrong plain text response. 2017-12-18 19:15:19 +01:00
Jocelyn Fiat
36eeff9285 Fixed the websocket example to also work for remote client.
(i.e not from the same machine).
2017-12-18 14:52:09 +01:00
Jocelyn Fiat
e9292b3eac Reverted last change as error_message on curl is not available for 17.05. 2017-12-01 12:05:01 +01:00
Jocelyn Fiat
30625d460f Added curl error message to the response error message (in addition to just the curl error code). 2017-11-30 20:08:58 +01:00
Jocelyn Fiat
24eb0a4002 Updated changelog 2017-11-30 15:05:54 +01:00
Jocelyn Fiat
7d738a164d Added missing comments. 2017-11-30 14:54:46 +01:00
Jocelyn Fiat
1037256ea6 Refactored using an abstraction WSF_PROTECTION.
This protection could be implemented with a regular expression,
    or using another solution (as manual parsing).
  Also, when a protection detects an issue, instead of returning empty string,
    it returns Void. If the value is a multiple string value, if an item is detected for an issue,
    the returned multiple string value is now Void.
  This abstraction will allow to return either Void, or a "corrected" value,
    for instance the string value, without the detected "<script..>..</script>" text.
  TODO: improve the WSF_PROTECTION_REGEXP to allow replacement strategy.
2017-11-27 15:44:19 +01:00
Jocelyn Fiat
4d79bba04b Merge branch 'ewf_xss' 2017-11-23 11:50:47 +01:00
jvelilla
5de024923e Updated xss support.
Added a new library wsf_security.
Updated test cases to cover protections policy.
Added a simple filter using an XSS implementation with WSF_XSS_REQUEST, but
it's possible to build custom filters and request using different protection patterns.
2017-11-22 17:22:02 -03:00
jvelilla
8b90241986 Moved XSS protection to WSF_EXTENSION.
Updated code to protect meta_variables.
Fixed typos.
Updated ecf's to use 1-16-0.
2017-11-13 15:06:02 -03:00
Jocelyn Fiat
da1c0b8545 Fixed typo in script. 2017-11-13 19:01:16 +01:00
Jocelyn Fiat
603bedf71d Reverted change that made WSF_URI_FILTER_HANDLER and WSF_URI_TEMPLATE_FILTER_HANDLER inheriting from WSF_EXECUTE_FILTER_HANDLER as it breaks existing projects using EiffelWeb. 2017-11-13 18:58:29 +01:00
Jocelyn Fiat
5fedad7f2e Updated Changelog. 2017-11-13 12:31:19 +01:00
Jocelyn Fiat
e83f5654d8 Updated NOTIFICATION_SMTP_MAILER to follow the EiffelNet EMAIL design. 2017-11-13 12:29:16 +01:00
jvelilla
25446cac12 Initial import WSF XSS protection.
Added an utility class to get safe query and form parameters.
Added a new WSF_XSS_REQUEST to use safe parameters.
Added a filter WSF_XSS_FILTER using WSF_XSS_REQUEST.
Added test cases

Signed-off-by: jvelilla <javier.hector@gmail.com>
2017-11-10 10:37:32 -03:00
Jocelyn Fiat
ccff084642 Updated travis CI config to use install script. 2017-11-08 10:20:50 +01:00
Jocelyn Fiat
830adbe10c Fixed response handlers compilation. 2017-11-07 23:52:42 +01:00
Jocelyn Fiat
e6d998953e Updated changelog. 2017-11-07 23:17:39 +01:00
88 changed files with 2787 additions and 646 deletions

View File

@@ -1,10 +1,8 @@
language: eiffel
before_script:
- export current_dir=$PWD ; echo current_dir=$current_dir ; cd ..
- export ISE_VERSION=17.05; export ISE_BUILD=100416
- curl -sSL http://downloads.sourceforge.net/eiffelstudio/Eiffel_${ISE_VERSION}_gpl_${ISE_BUILD}-linux-x86-64.tar.bz2 | tar -x --bzip2
- export ISE_EIFFEL=$PWD/Eiffel_${ISE_VERSION} ; export ISE_PLATFORM=linux-x86-64
- export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin:$PATH:$ISE_EIFFEL/tools/spec/$ISE_PLATFORM/bin
- curl -sSL https://www.eiffel.org/setup/install.sh | bash > eiffel.rc
- source ./eiffel.rc
- echo `ec -version`
- cd $current_dir
- echo Check projects compilation status...

View File

@@ -7,13 +7,54 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
## [Unreleased]
### Added
### Changed
### Deprecated
### Removed
### Fixed
### Security
## [v1.1.0] - 2018-10-29
### Added
- `examples`: Added an example to run the debug app with apache2+libfcgi inside a docker container.
- `wsf`: Updated the default rescue response (i.e when exception or bad internal error occurs). Factorized the implementation in `WGI_RESCUE_EXECUTION`, and now by redefining the `WGI_EXECUTION.execute_rescue (...)` procedure, it is possible to have a custom response on such rescued execution.
- `http`: Added `HTTP_COOKIE.set_expiration_from_max_age`, to add the "Expiration:" based on the max-age value.
### Changed
- `wsf`: Adopted the nanoseconds timeout precision. And in config file added support for ns, us, ms, s timeout precision (without indication, it uses `seconds` precision).
### Deprecated
### Removed
### Fixed
- `websocket`: Allow (websocket) upgrade even without persistent connection for normal http request.
(note: this allows to use websocket in single-threaded mode, and avoid the keep-alive-timeout delay before websocket begins its execution)
- `http`: Fixed `HTTP_HEADER.put_raw_header (..)` by ignoring any empty line of the argument value. (note: "%R" is considered as empty line here.)
- `jwt`: updated to make JWT algorithm support more flexible, and simple to extend with specific algorithm.
- `httpd`, `websocket`: Fixed setting of socket.timeout in httpd (it was not correctly set).
### Security
## [v1.0.6] - 2018-02-05
### Added
- `jwt`: new JSON Web Token (JWT) library (supports for claim exp, iat, nbf, iss, aud).
- `http_client`: added support for ciphers setting in the libcurl implementation only.
- `http_client`: added convenient `get` and `custom` functions on HTTP_CLIENT directly.
- `http_client`: added convenient `get` and `custom` functions on `HTTP_CLIENT` directly.
- `wsf`: added `WSF_EXECUTE_HANDLER`, and `WSF_CGI_HANDLER`. Demonstration of `WSF_CGI_HANDLER` in the new `tools/httpd` project.
- `wsf_security`: new security library, providing support for XSS injection protection and similar.
- `wsf`: Support persistent connection, even in single thread mode (i.e concurrency=none).
Warning: as there is no concurrent request handling in single threaded mode, it is recommended to either set the keep_alive_timeout to a small value, or disable persistent connection by setting max_keep_alive_requests to 0.
Accept -1 as value of max_keep_alive_requests to have unlimited number of request in the same persistent connection.
- `wsf_compression`: Introduced `WSF_COMPRESSION` and applied to `WSF_*_WITH_COMPRESSION` classes. Added `simple_compression` example.
- `websocket`: added `on_timer` solution to allow the server to check for external events and send notification to websocket clients.
- `wsf`: Added routing condition mapping.
- `wsf_extension`: added handler to add support for CGI scripts.
- Added a new tool `httpd` which is a basic httpd server product (with file server and CGI handler).
- `wsf_security`: added security protections such as XSS injection protection support.
### Changed
- adopted ecf version 1-16-0 and use a single .ecf file (the -safe.ecf are now redirection to normal .ecf)
- `wsf_html`: Made interface of wsf forms and widgets a bit more flexible by accepting `READABLE_STRING_GENERAL`.
### Deprecated
- removed support for Eiffel version before 17.05 .
- SSL 2 or 3 is obsolete and will raise an exception if used.
@@ -21,8 +62,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
### Fixed
- Removed a few obsolete calls.
- `http_client`: Added support for multiple file in form data. Made clear what is the meaning of `upload_filename`, `upload_data` and `form_data`.
- `authentication`: HTTP_AUTHORIZATION acceps now READABLE_STRING_GENERAL for username and password argument.
- `authentication`: `HTTP_AUTHORIZATION` acceps now `READABLE_STRING_GENERAL` for username and password argument.
- `http_client`: fixed curl implementation by setting `Content-Type` to `x-www-form-urlencoded` (if not set) when POST send data as `x-www-form-urlencoded`.
- `notification_email`: fixed the SMTP support for multiple recipients address.
- `http_network`: use proper ciphers settings for libcurl implementation.
- `http_client`: Improved support of absolute/relative https:// and http:// in `http_client`.
- `json_encoder.e`: Properly JSON encode null character as \u0000 .
### Security

View File

@@ -13,14 +13,14 @@ Currently EWF offers a collection of Eiffel libraries designed to be integrated
There is a growing ecosystem around EWF, that provides useful components:
* OpenID and OAuth consumer library
* Various hypermedia format such as HAL, Collection+json,
* Various hypermedia format such as HAL, Collection+json, ...
* Websocket server and client
* Template engine
* API Auto-documentation with swagger
* A simple experimental CMS.
* ...
So if you want to build a website, a web api, RESTful service, or even if you want to consume other web api, EWF is a solution.
So if you want to build a website, a web api, RESTful service, ... or even if you want to consume other web api, EWF is a solution.
EWF brings with it all the advantages of the Eiffel technology and tools with its powerful features such as Design by Contract, debugging, testing tools which enable to build efficient systems expected to be repeatedly refined, extended, and improved in a predictable and controllable way so as to become with time bugfree systems. Enjoy the full power of debugging your web server application from the IDE.
@@ -51,11 +51,11 @@ Tasks and issues are managed with github issue system
## How to get the source code?
Using git
* git clone https://github.com/EiffelWebFramework/EWF.git
* `git clone https://github.com/EiffelWebFramework/EWF.git`
* And to build the required and related Clibs
* cd contrib/ise_library/cURL
* geant compile
* `cd contrib/ise_library/cURL`
* `geant compile`
## Libraries under 'library'
@@ -64,7 +64,7 @@ Using git
* connectors: various web server connectors for EWSGI
* libfcgi: Wrapper for libfcgi SDK
* __wsf__: Web Server Framework [read more](library/server/wsf)
* __router__: URL dispatching/routing based on uri, uri_template, or custom [read more](library/server/wsf/router)
* __router__: URL dispatching/routing based on `uri`, `uri_template`, or custom [read more](library/server/wsf/router)
### protocol
* __http__: HTTP related classes, constants for status code, content types, ... [read more](library/network/protocol/http)

View File

@@ -14,3 +14,9 @@ port: 9000
repo: https://github.com/EiffelWebFramework/EWF
version: v1
download: https://github.com/EiffelWebFramework/EWF/archive/v1.zip
plugins:
- jekyll-relative-links
relative_links:
enabled: true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

View File

@@ -94,11 +94,12 @@ The **WSF_RESPONSE** provides features to define the response with information s
**APPLICATION** is the root class of our example, it launches the application, using the corresponding connector, Which connector? this depends how do you want to run it `cgi`, `fcgi`, `standalone`. For development is recommended to use a standalone web server written in Eiffel, and run the execution within the EiffelStudio debugger. For production fcgi (or cgi) using Apache or another popular web server.
![Launcher Hierarchy](./Launcher Hierarchy.png "Launcher Hierarchy")
![Launcher Hierarchy](images/Launcher_Hierarchy.png "Launcher Hierarchy")
**WS_LAUNCHABLE_SERVICE** inherit from **WS_SERVICE** class, which is a marker interface in EWF. And also provides a way to launch our application using different kind of connectors. The class **WSF_DEFAULT_SERVICE_I**, inherit from **WS_LAUNCHABLE_SERVICE** and has a formal generic that should conform to **WSF_SERVICE_LAUNCHER [WSF_EXECUTION]**. Below a [BON diagram](http://www.bon-method.com/index_normal.htm) showing one of the possible options.
![Standalone Launcher](./WSF_SERVICE_LAUNCHER_STANDALONE.png "Standalone Hierarchy")
![Standalone Launcher](images/WSF_SERVICE_LAUNCHER_STANDALONE.png "Standalone Hierarchy")
Other connectors:
**WSF_STANDALONE_SERVICE_LAUNCHER**
@@ -111,7 +112,7 @@ The **APPLICATION_EXECUTION** class inherits from **WSF_EXECUTION** interface,
In the **APPLICATION_EXECUTION** class class you will need to implement the **execute** feature, get data from the request *req* and write the response in *res*.
![Execution Hierarchy](./APPLICATION_EXECUTION.png "Application Execution ")
![Execution Hierarchy](images/APPLICATION_EXECUTION.png "Application Execution ")
The WSF_EXECUTION instance, in this case ```APPLICATION_EXECUTION``` is created per request, with two main attributes request: ```WSF_REQUEST``` and response: ```WSF_RESPONSE```.

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,50 @@
FROM debian
#ubuntu:xenial
MAINTAINER Jocelyn Fiat <jfiat@eiffel.com>
LABEL description="EiffelWeb debug example hosted using apache2+libfcgi"
RUN apt-get update \
&& apt-get -y install \
curl bzip2 make gcc git-core \
apache2 libapache2-mod-fcgid libfcgi-dev \
&& rm -rf /var/lib/apt/lists/*
#RUN apt-get update && apt-get -y install tmux git-all vim && rm -rf /var/lib/apt/lists/*
EXPOSE 80
RUN a2enmod rewrite suexec include fcgid
RUN export uid=1000 gid=1000 && \
mkdir -p /home/eifweb && \
echo "eifweb:x:${uid}:${gid}:eifweb,,,:/home/eifweb:/bin/bash" >> /etc/passwd && \
echo "eifweb:x:${uid}:" >> /etc/group && \
chown ${uid}:${gid} -R /home/eifweb
USER eifweb
ENV HOME /home/eifweb
ENV WEBDIR /home/eifweb/www
WORKDIR $HOME
# Create expected folders
RUN mkdir $WEBDIR
#Build the debug EiffelWeb example and copy the executable to $HOME/
COPY files/build_service_fcgi $HOME/build_service_fcgi
USER root
RUN chown eifweb:eifweb $HOME/build_service_fcgi && chmod 700 $HOME/build_service_fcgi
USER eifweb
RUN $HOME/build_service_fcgi $HOME/www
USER root
COPY ./files/httpd.conf /etc/apache2/sites-enabled/000-default.conf
COPY ./files/html/.htaccess $WEBDIR/html/.htaccess
COPY ./files/html/index.html $WEBDIR/html/index.html
RUN echo > $WEBDIR/html/service.ews
RUN chown www-data:www-data -R $WEBDIR
#Setup apache as foreground (for docker purpose)
RUN mkdir -p /etc/service/apache
ADD ./files/apache.sh /etc/service/apache/run
RUN chmod +x /etc/service/apache/run
ENTRYPOINT ["/etc/service/apache/run"]

25
examples/docker/README.md Normal file
View File

@@ -0,0 +1,25 @@
Docker container for EiffelWeb debug example with apache2+libfcgi.
==================================================================
This example demonstrates the use of EiffelWeb with Apache2, using the libfcgi connector.
For that, it is using the Docker solution to show the compilation and configuration steps.
To build the docker image:
```
docker build -t local/ewf-debug-httpd .
```
To run the docker image in a self-destroyed container:
```
docker run --rm -dit -p 8080:80 --name my-ewf-debug local/ewf-debug-httpd
```
Notes:
- `--rm` : to remove the container after the execution
- `-p 8080:80` : to map internal listening port 80 to localhost port 8080.
- on Linux, you may need to use `sudo` to be able to use `docker`
- depending on the docker installation, you may need to add an extra `-e ISE_PLATFORM=linux-x86` to force execution in 32bits.
- This docker example is simple on purpose. For production it should be improved and optimized to keep the image smaller and more customizable.
- For more advanced Docker usage, please refer to official https://www.docker.com/ website.

View File

@@ -0,0 +1,3 @@
#!/bin/sh
exec /usr/sbin/apache2ctl -D FOREGROUND

View File

@@ -0,0 +1,25 @@
#!/bin/bash
web_dir=$1
mkdir $web_dir/bin
mkdir $web_dir/html
# Install latest EiffelStudio suite.
curl -sSL https://www.eiffel.org/setup/install.sh > install_eiffel.sh \
&& bash ./install_eiffel.sh latest > $HOME/eiffel.rc
# Setup Eiffel environment
source $HOME/eiffel.rc
# Get source code
git clone https://github.com/EiffelWebFramework/EWF.git $web_dir/src
# Build executable
eiffel build -v --target debug_libfcgi $web_dir/src/examples/debug/debug.ecf $web_dir/html/service.fcgi
# Clean files
rm -rf $ISE_EIFFEL
rm -rf $web_dir/src
exit $?

View File

@@ -0,0 +1,20 @@
<IfModule mod_fcgid.c>
AddHandler fcgid-script .ews
FcgidWrapper /home/eifweb/www/html/service.fcgi .ews
</IfModule>
Options +ExecCGI +Includes +FollowSymLinks
<IfModule mod_rewrite.c>
RewriteEngine on
#RewriteRule ^.*$ maintenance.html [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^$ service.ews/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ service.ews/$1 [C]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
</IfModule>

View File

@@ -0,0 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>EiffelWeb running on Apache2+libfcgi</title>
</head>
<body>
EiffelWeb debug example hosted on apache2+libfcgi .
<ul>
<li>Send a GET request <a href="get?foo=bar">get?foo=bar</a></li>
<li>Send a POST request <form action="post?foo=bar" method="POST">
<input type="text" name="one" value="value_1"/>
<input type="text" name="two" value="value_2"/>
<input type="submit" value="Submit"/>
</form>
</li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,53 @@
<IfModule mod_fcgid.c>
FcgidIdleTimeout 60
FcgidBusyScanInterval 120
FcgidProcessLifeTime 1800
#7200
FcgidMaxProcesses 10
FcgidMaxProcessesPerClass 100
FcgidMinProcessesPerClass 100
FcgidConnectTimeout 8
FcgidIOTimeout 3000
FcgidBusyTimeout 300
FcgidPassHeader Authorization
FcgidMaxRequestLen 10000000
FcgidMaxRequestsPerProcess 100
</IfModule>
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin webmaster@localhost
DocumentRoot /home/eifweb/www/html
<Directory /home/eifweb/www/html>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>

View File

@@ -26,22 +26,27 @@ feature -- Basic operations
do
-- To send a response we need to setup, the status code and
-- the response headers.
if request.path_info.same_string_general ("/app") then
s := websocket_app_html (9090)
if request.path_info.same_string_general ("/favicon.ico") then
response.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Length", "0"]>>)
else
s := "Hello World!"
create dt.make_now_utc
s.append (" (UTC time is " + dt.rfc850_string + ").")
s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
if request.path_info.same_string_general ("/app") then
s := websocket_app_html (request.server_name, request.server_port)
else
s := "Hello World!"
create dt.make_now_utc
s.append (" (UTC time is " + dt.rfc850_string + ").")
s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
response.set_status_code ({HTTP_STATUS_CODE}.ok)
response.header.put_content_type_text_html
response.header.put_content_length (s.count)
if request.is_keep_alive_http_connection then
response.header.put_connection_keep_alive
end
response.put_string (s)
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
response.set_status_code ({HTTP_STATUS_CODE}.ok)
response.header.put_content_type_text_html
response.header.put_content_length (s.count)
if request.is_keep_alive_http_connection then
response.header.put_connection_keep_alive
end
response.put_string (s)
end
feature -- Websocket execution
@@ -184,7 +189,7 @@ feature -- Command
feature -- HTML Resource
websocket_app_html (a_port: INTEGER): STRING
websocket_app_html (a_host: STRING; a_port: INTEGER): STRING
do
Result := "[
<!DOCTYPE html>
@@ -198,7 +203,7 @@ $(document).ready(function() {
function connect(){
var host = "##WSSCHEME##://127.0.0.1:##PORTNUMBER##/app";
var host = "##WSSCHEME##://##HOSTNAME##:##PORTNUMBER##/app";
try{
@@ -280,6 +285,7 @@ body {font-family:Arial, Helvetica, sans-serif;}
</body>
</html>
]"
Result.replace_substring_all ("##HOSTNAME##", a_host)
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
if request.is_https then
Result.replace_substring_all ("##HTTPSCHEME##", "https")

View File

@@ -4,10 +4,10 @@ port=9090
max_concurrent_connections=100
max_tcp_clients=100
socket_timeout=30
socket_recv_timeout=5
socket_recv_timeout=100 ms
#Persistent connections
keep_alive_timeout=2
keep_alive_timeout=50 ms
max_keep_alive_requests=-1
#SSL

View File

@@ -151,14 +151,14 @@ feature -- Settings
timeout: INTEGER
-- HTTP transaction timeout in seconds.
--| 0 means it nevers timeout
--| 0 means it never timeouts
do
Result := session.timeout
end
connect_timeout: INTEGER
-- HTTP connection timeout in seconds.
--| 0 means it nevers timeout
-- HTTP connection timeout in milliseconds.
--| 0 means it never timeouts
do
Result := session.connect_timeout
end
@@ -208,7 +208,7 @@ feature -- Settings
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -31,9 +31,12 @@ feature {NONE} -- Initialization
apply_workaround
-- Due to issue with Eiffel cURL on Windows 32bits
-- we need to do the following workaround
-- we need to do the following workaround.
local
add: detachable INET_ADDRESS
once
if attached (create {INET_ADDRESS_FACTORY}).create_localhost then
if {PLATFORM}.is_windows then
add := (create {INET_ADDRESS_FACTORY}).create_localhost
end
end
@@ -449,7 +452,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -64,7 +64,7 @@ feature {NONE} -- Internal
create Result.make_client_by_port (a_port, a_host)
end
Result.set_connect_timeout (connect_timeout)
Result.set_timeout (timeout)
Result.set_timeout_ns (Result.seconds_to_nanoseconds (timeout))
Result.connect
end
end
@@ -887,7 +887,7 @@ feature {NONE} -- Helpers
invariant
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -27,10 +27,22 @@
<custom name="ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<cluster name="ssl_network" location="$|ssl\" recursive="false">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/http_stream_secure_socket_ext\.e$</exclude>
<condition>
<version type="compiler" max="18.05.0.0"/>
</condition>
</file_rule>
</cluster>
<cluster name="ssl_network_until_18_05" location="$|ssl\until_18.05" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
<version type="compiler" max="18.05.0.0"/>
</condition>
</cluster>
</cluster>
</target>

View File

@@ -2,22 +2,9 @@ note
description: "[
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTP_STREAM_SECURE_SOCKET_EXT
feature {NONE} -- SSL bridge
ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER
do
Result := a_ssl.write (a_pointer, a_byte_count)
if a_ssl.was_error then
if Result >= 0 then
Result := -1
end
end
end
end

View File

@@ -0,0 +1,23 @@
note
description: "[
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTP_STREAM_SECURE_SOCKET_EXT
feature {NONE} -- SSL bridge
ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER
do
Result := a_ssl.write (a_pointer, a_byte_count)
if a_ssl.was_error then
if Result >= 0 then
Result := -1
end
end
end
end

View File

@@ -197,6 +197,24 @@ feature -- Change Element
expiration_set: attached expiration as l_expiration and then l_expiration.same_string (date_to_rfc1123_http_date_format (a_date))
end
set_expiration_from_max_age
-- Set `expiration` value from `max_age`.
local
dt: DATE_TIME
do
if max_age < 0 then
unset_expiration
else
if max_age = 0 then
create dt.make_from_epoch (0)
else
create dt.make_now_utc
dt.second_add (max_age)
end
set_expiration_date (dt)
end
end
set_path (a_path: READABLE_STRING_8)
-- Set `path' to `a_path'.
do

View File

@@ -228,7 +228,9 @@ feature -- Header: merging
if line [line.count] = '%R' then
line.remove_tail (1)
end
put_header (line)
if not line.is_empty then
put_header (line)
end
end
end
end

View File

@@ -213,6 +213,7 @@ feature -- Content related header
--| note: see `put_content_type_with_charset' for examples.
local
s: STRING_8
v: READABLE_STRING_8
do
if a_params /= Void and then not a_params.is_empty then
create s.make_from_string (a_content_type)
@@ -224,9 +225,14 @@ feature -- Content related header
s.append_character (' ')
s.append (nv.name)
s.append_character ('=')
s.append_character ('%"')
s.append (nv.value)
s.append_character ('%"')
v := nv.value
if v.has(' ') or v.has ('%T') or v.has ('=') then
s.append_character ('%"')
s.append (v)
s.append_character ('%"')
else
s.append (v)
end
end
end
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)

View File

@@ -95,40 +95,67 @@ feature -- Basic operation
l_email: EMAIL
h: STRING
i: INTEGER
lst: LIST [READABLE_STRING_8]
do
create l_email.make_with_entry (a_email.from_address, addresses_to_header_line_value (a_email.to_addresses))
if attached a_email.reply_to_address as l_reply_to then
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
end
if attached a_email.cc_addresses as lst then
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_cc, addresses_to_header_line_value (lst))
end
if attached a_email.bcc_addresses as lst then
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_bcc, addresses_to_header_line_value (lst))
end
l_email.set_message (a_email.content)
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
create h.make_empty
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
l_email.add_header_entry ("Date", h)
if attached a_email.additional_header_lines as lst then
across
lst as ic
loop
h := ic.item
i := h.index_of (':', 1)
if i > 0 then
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
else
check is_header_line: False end
lst := a_email.to_addresses
if lst.is_empty then
-- Error ...
else
-- With EMAIL, there should be a unique recipient at creation.
create l_email.make_with_entry (a_email.from_address, lst.first)
if lst.count > 1 then
from
lst.start
lst.forth
until
lst.off
loop
l_email.add_recipient_address (lst.item)
lst.forth
end
end
end
if attached a_email.reply_to_address as l_reply_to then
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
end
smtp_send_email (l_email)
if attached a_email.cc_addresses as l_cc_addresses then
across
l_cc_addresses as ic
loop
l_email.add_recipient_address_in_cc (ic.item)
end
end
if attached a_email.bcc_addresses as l_bcc_addresses then
across
l_bcc_addresses as ic
loop
l_email.add_recipient_address_in_bcc (ic.item)
end
end
l_email.set_message (a_email.content)
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
create h.make_empty
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
l_email.add_header_entry ("Date", h)
if attached a_email.additional_header_lines as l_lines then
across
l_lines as ic
loop
h := ic.item
i := h.index_of (':', 1)
if i > 0 then
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
else
check is_header_line: False end
end
end
end
smtp_send_email (l_email)
end
end
feature {NONE} -- Implementation
@@ -182,4 +209,3 @@ note
Customer support http://support.eiffel.com
]"
end

View File

@@ -64,14 +64,16 @@ feature -- Conversion
encoded_string (a_secret: READABLE_STRING_8): STRING
local
alg, sign: READABLE_STRING_8
sign, alg_name: READABLE_STRING_8
alg: JWT_ALG
l_enc_payload, l_enc_header: READABLE_STRING_8
do
reset_error
alg := header.algorithm
if not is_supporting_signature_algorithm (alg) then
report_unsupported_alg_error (alg)
alg := alg_hs256 -- Default ...
alg_name := header.algorithm
alg := algorithms [alg_name]
if alg = Void then
report_unsupported_alg_error (alg_name)
alg := algorithms.hs256 -- Default ...
end
l_enc_header := base64url_encode (header.string)
l_enc_payload := base64url_encode (claimset.string)
@@ -94,12 +96,12 @@ feature -- Element change
set_algorithm_to_hs256
do
set_algorithm (alg_hs256)
set_algorithm (algorithms.hs256.name)
end
set_algorithm_to_none
do
set_algorithm (alg_none)
set_algorithm (algorithms.none.name)
end
end

View File

@@ -16,12 +16,15 @@ feature {NONE} -- Initialization
default_create
do
create algorithms
create header
create claimset
end
feature -- Access
algorithms: JWT_ALGORITHMS
header: JWT_HEADER
claimset: JWT_CLAIMSET
@@ -91,6 +94,11 @@ feature -- status report
Result := attached errors as errs and then across errs as ic some attached {JWT_UNSUPPORTED_ALG_ERROR} ic.item end
end
has_mismatched_alg_error: BOOLEAN
do
Result := attached errors as errs and then across errs as ic some attached {JWT_MISMATCHED_ALG_ERROR} ic.item end
end
has_unverified_token_error: BOOLEAN
do
Result := attached errors as errs and then across errs as ic some attached {JWT_UNVERIFIED_TOKEN_ERROR} ic.item end

View File

@@ -0,0 +1,27 @@
note
description: "Summary description for {JWT_ALG}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
JWT_ALG
feature -- Access
name: READABLE_STRING_8
deferred
end
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
deferred
end
feature -- Status report
is_none: BOOLEAN
-- Is Current algorithm is "none" ?
do
end
end

View File

@@ -0,0 +1,57 @@
note
description: "Summary description for {JWT_ALG_HS256}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
JWT_ALG_HS256
inherit
JWT_ALG
feature -- Access
name: STRING = "hs256"
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
do
Result := base64_hmacsha256 (a_message, a_secret)
end
feature {NONE} -- Implementation
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
local
hs256: HMAC_SHA256
do
create hs256.make_ascii_key (a_secret)
hs256.update_from_string (s)
-- if Version >= EiffelStudio 18.01 then
-- Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
-- else
Result := base64_bytes_encoded_string (hs256.digest)
-- end
end
base64_bytes_encoded_string (a_bytes: SPECIAL [NATURAL_8]): STRING_8
-- Base64 string from `a_bytes`.
--| Note: to be removed when 18.01 is not latest release anymore.
local
s: STRING
i,n: INTEGER
do
from
i := 1
n := a_bytes.count
create s.make (n)
until
i > n
loop
s.append_code (a_bytes[i - 1])
i := i + 1
end
Result := (create {BASE64}).encoded_string (s)
end
end

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {JWT_ALG_NONE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
JWT_ALG_NONE
inherit
JWT_ALG
redefine
is_none
end
feature -- Access
name: STRING = "none"
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
do
create Result.make_empty
end
feature -- Status report
is_none: BOOLEAN = True
-- Is Current algorithm is "none" ?
end

View File

@@ -0,0 +1,98 @@
note
description: "Summary description for {JWT_ALGORITHMS}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
JWT_ALGORITHMS
inherit
ANY
redefine
default_create
end
create
default_create
feature {NONE} -- Initialization
default_create
do
create items.make_caseless (2)
register_algorithm (hs256)
register_algorithm (none)
-- TODO: check if this is acceptable default.
set_default_algorithm ({JWT_ALG_HS256}.name)
end
feature -- Access
hs256: JWT_ALG_HS256
do
create Result
end
none: JWT_ALG_NONE
do
create Result
end
feature -- Access
default_algorithm: JWT_ALG
do
if attached internal_default_alg_name as l_alg_name then
Result := algorithm (l_alg_name)
end
if Result = Void then
Result := none
end
end
algorithm alias "[]" (a_name: READABLE_STRING_GENERAL): detachable JWT_ALG
do
Result := items [a_name]
end
feature -- Element change
register_algorithm (alg: attached like algorithm)
do
items [alg.name] := alg
end
unregister_algorithm (a_alg_name: READABLE_STRING_GENERAL)
do
items.remove (a_alg_name)
end
set_default_algorithm (a_alg_name: detachable READABLE_STRING_GENERAL)
do
if
a_alg_name = Void or else
not is_supported_algorithm (a_alg_name)
then
internal_default_alg_name := Void
else
internal_default_alg_name := a_alg_name
end
end
feature -- Status report
is_supported_algorithm (a_name: READABLE_STRING_GENERAL): BOOLEAN
do
Result := items.has (a_name)
end
feature {NONE} -- Implementation
items: STRING_TABLE [attached like algorithm]
internal_default_alg_name: detachable READABLE_STRING_GENERAL
invariant
end

View File

@@ -1,275 +0,0 @@
note
description: "JSON Web Token encoder"
date: "$Date$"
revision: "$Revision$"
class
JWT_ENCODER
feature -- Basic operations
encoded_values (a_values: STRING_TABLE [READABLE_STRING_GENERAL]; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
local
j: JSON_OBJECT
do
create j.make_with_capacity (a_values.count)
across
a_values as ic
loop
j.put_string (ic.item, ic.key)
end
Result := encoded_json (j, a_secret, a_algo)
end
encoded_json (a_json: JSON_OBJECT; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
local
vis: JSON_PRETTY_STRING_VISITOR
s: STRING
do
create s.make_empty
create vis.make (s)
vis.visit_json_object (a_json)
Result := encoded_string (s, a_secret, a_algo)
end
encoded_string (a_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
local
alg, sign: STRING_8
l_enc_payload, l_enc_header: READABLE_STRING_8
do
reset_error
if a_algo.is_case_insensitive_equal_general (alg_hs256) then
alg := alg_hs256
elseif a_algo.is_case_insensitive_equal_general (alg_none) then
alg := alg_none
else
report_unsupported_alg_error (a_algo)
alg := alg_hs256 -- Default ...
end
l_enc_header := base64url_encode (header ("JWT", alg))
l_enc_payload := base64url_encode (a_payload)
sign := signature (l_enc_header, l_enc_payload, a_secret, alg)
create Result.make (l_enc_header.count + 1 + l_enc_payload.count + 1 + sign.count)
Result.append (l_enc_header)
Result.append_character ('.')
Result.append (l_enc_payload)
Result.append_character ('.')
Result.append (sign)
end
decoded_string (a_token: READABLE_STRING_8; a_secret: READABLE_STRING_8; a_algo: detachable READABLE_STRING_8): detachable STRING
local
i,j,n: INTEGER
alg, l_enc_payload, l_enc_header, l_signature: READABLE_STRING_8
do
reset_error
n := a_token.count
i := a_token.index_of ('.', 1)
if i > 0 then
j := a_token.index_of ('.', i + 1)
if j > 0 then
l_enc_header := a_token.substring (1, i - 1)
l_enc_payload := a_token.substring (i + 1, j - 1)
l_signature := a_token.substring (j + 1, n)
Result := base64url_decode (l_enc_payload)
alg := a_algo
if alg = Void then
alg := signature_algorithm_from_encoded_header (l_enc_header)
if alg = Void then
-- Use default
alg := alg_hs256
end
end
check alg_set: alg /= Void end
if alg.is_case_insensitive_equal (alg_hs256) then
alg := alg_hs256
elseif alg.is_case_insensitive_equal (alg_none) then
alg := alg_none
else
alg := alg_hs256
report_unsupported_alg_error (alg)
end
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_secret, alg)) then
report_unverified_token_error
end
else
report_invalid_token
end
else
report_invalid_token
end
end
feature -- Error status
error_code: INTEGER
-- Last error, if any.
has_error: BOOLEAN
-- Last `encoded_string` reported an error?
do
Result := error_code /= 0
end
has_unsupported_alg_error: BOOLEAN
do
Result := error_code = unsupported_alg_error
end
has_unverified_token_error: BOOLEAN
do
Result := error_code = unverified_token_error
end
has_invalid_token_error: BOOLEAN
do
Result := error_code = invalid_token_error
end
feature {NONE} -- Error reporting
reset_error
do
error_code := 0
end
report_unsupported_alg_error (alg: READABLE_STRING_8)
do
error_code := unsupported_alg_error
end
report_unverified_token_error
do
error_code := unverified_token_error
end
report_invalid_token
do
error_code := invalid_token_error
end
feature {NONE} -- Constants
unsupported_alg_error: INTEGER = -2
unverified_token_error: INTEGER = -4
invalid_token_error: INTEGER = -8
alg_hs256: STRING = "HS256"
-- HMAC SHA256.
alg_none: STRING = "none"
-- for unsecured token.
feature -- Conversion
header (a_type: detachable READABLE_STRING_8; alg: READABLE_STRING_8): STRING
do
create Result.make_empty
Result.append ("{%"typ%":%"")
if a_type /= Void then
Result.append (a_type)
else
Result.append ("JWT")
end
Result.append ("%",%"alg%":%"")
Result.append (alg)
Result.append ("%"}")
end
feature {NONE} -- Conversion
signature_algorithm_from_encoded_header (a_enc_header: READABLE_STRING_8): detachable STRING_8
local
jp: JSON_PARSER
do
create jp.make_with_string (base64url_decode (a_enc_header))
jp.parse_content
if
attached jp.parsed_json_object as jo and then
attached {JSON_STRING} jo.item ("alg") as j_alg
then
Result := j_alg.unescaped_string_8
end
end
feature -- Base64
base64url_encode (s: READABLE_STRING_8): STRING_8
local
urlencoder: URL_ENCODER
base64: BASE64
do
create urlencoder
create base64
Result := urlsafe_encode (base64.encoded_string (s))
end
feature {NONE} -- Implementation
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: READABLE_STRING_8): STRING_8
local
s: STRING
do
if alg = alg_none then
create Result.make_empty
else
create s.make (a_enc_header.count + 1 + a_enc_payload.count)
s.append (a_enc_header)
s.append_character ('.')
s.append (a_enc_payload)
if alg = alg_hs256 then
Result := base64_hmacsha256 (s, a_secret)
else
Result := base64_hmacsha256 (s, a_secret)
end
Result := urlsafe_encode (Result)
end
end
base64url_decode (s: READABLE_STRING_8): STRING_8
local
urlencoder: URL_ENCODER
base64: BASE64
do
create urlencoder
create base64
Result := base64.decoded_string (urlsafe_decode (s))
end
urlsafe_encode (s: READABLE_STRING_8): STRING_8
do
create Result.make_from_string (s)
Result.replace_substring_all ("=", "")
Result.replace_substring_all ("+", "-")
Result.replace_substring_all ("/", "_")
end
urlsafe_decode (s: READABLE_STRING_8): STRING_8
local
i: INTEGER
do
create Result.make_from_string (s)
Result.replace_substring_all ("-", "+")
Result.replace_substring_all ("_", "/")
from
i := Result.count \\ 4
until
i = 0
loop
i := i - 1
Result.extend ('=')
end
end
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
local
ut: JWT_UTILITIES
do
create ut
Result := ut.base64_hmacsha256 (s, a_secret)
end
end

View File

@@ -9,6 +9,20 @@ class
inherit
JWT_UTILITIES
redefine
default_create
end
feature {NONE} -- Initialization
default_create
do
create algorithms
end
feature -- Settings
algorithms: JWT_ALGORITHMS
feature -- Access
@@ -18,11 +32,12 @@ feature -- Access
-- WARNING: passing Void for `a_alg` is not safe, as the server should know which alg he used for tokens,
-- leaving the possibility to use the header alg is dangerous as client may use "none" and then bypass verification!
require
a_valid_alg: a_alg /= Void implies is_supporting_signature_algorithm (a_alg)
a_valid_alg: a_alg /= Void implies algorithms.is_supported_algorithm (a_alg)
local
jws: JWS
i,j,n: INTEGER
alg, l_enc_payload, l_enc_header, l_signature: READABLE_STRING_8
alg_encoder: JWT_ALG
do
n := a_token_input.count
i := a_token_input.index_of ('.', 1)
@@ -43,17 +58,18 @@ feature -- Access
else
if alg = Void then
-- Use default
alg := alg_hs256
alg := algorithms.default_algorithm.name
end
end
jws.set_algorithm (alg)
check alg_set: alg /= Void end
if ctx = Void or else not ctx.validation_ignored then
if not is_supporting_signature_algorithm (alg) then
alg_encoder := algorithms [alg]
if alg_encoder = Void then
jws.report_unsupported_alg_error (alg)
alg := alg_hs256
alg_encoder := algorithms.default_algorithm
end
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg)) then
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg_encoder)) then
jws.report_unverified_token_error
end
if

View File

@@ -7,14 +7,6 @@ note
class
JWT_UTILITIES
feature -- Constants
alg_hs256: STRING = "HS256"
-- HMAC SHA256.
alg_none: STRING = "none"
-- for unsecured token.
feature -- Encoding
base64url_encode (s: READABLE_STRING_8): STRING_8
@@ -35,61 +27,21 @@ feature -- Encoding
Result.replace_substring_all ("/", "_")
end
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: READABLE_STRING_8): STRING_8
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: JWT_ALG): STRING_8
local
s: STRING
do
if alg.is_case_insensitive_equal (alg_none) then
if alg.is_none then
create Result.make_empty
else
create s.make (a_enc_header.count + 1 + a_enc_payload.count)
s.append (a_enc_header)
s.append_character ('.')
s.append (a_enc_payload)
if alg.is_case_insensitive_equal (alg_hs256) then
Result := base64_hmacsha256 (s, a_secret)
else
Result := base64_hmacsha256 (s, a_secret)
end
Result := urlsafe_encode (Result)
Result := urlsafe_encode (alg.encoded_string (s, a_secret))
end
end
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
local
hs256: HMAC_SHA256
do
create hs256.make_ascii_key (a_secret)
hs256.update_from_string (s)
-- if Version >= EiffelStudio 17.11 then
-- Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
-- else
Result := base64_bytes_encoded_string (hs256.digest)
-- end
end
feature {NONE} -- Implementation
base64_bytes_encoded_string (a_bytes: SPECIAL [NATURAL_8]): STRING_8
-- Base64 string from `a_bytes`.
--| Note: to be removed when 17.11 is not latest release anymore.
local
s: STRING
i,n: INTEGER
do
from
i := 1
n := a_bytes.count
create s.make (n)
until
i > n
loop
s.append_code (a_bytes[i - 1])
i := i + 1
end
Result := (create {BASE64}).encoded_string (s)
end
feature -- Decoding
base64url_decode (s: READABLE_STRING_8): STRING_8
@@ -119,20 +71,4 @@ feature -- Decoding
end
end
feature -- Signature
supported_signature_algorithms: LIST [READABLE_STRING_8]
-- Supported signature algorithm `alg`?
do
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (2)
Result.extend (alg_hs256)
Result.extend (alg_none)
end
is_supporting_signature_algorithm (alg: READABLE_STRING_8): BOOLEAN
-- Is supporting signature algorithm `alg`?
do
Result := across supported_signature_algorithms as ic some alg.is_case_insensitive_equal (ic.item) end
end
end

View File

@@ -0,0 +1,22 @@
note
description: "Summary description for {JWT_ALG_TEST}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
JWT_ALG_TEST
inherit
JWT_ALG
feature -- Access
name: STRING = "test"
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
do
Result := "TEST<<"+ a_message + ">>"
end
end

View File

@@ -16,6 +16,30 @@ inherit
feature -- Test
example
local
jwt: JWS
l_loader: JWT_LOADER
tok: STRING
do
create jwt.make_with_json_payload ("[
{"iss":"joe", "exp":1200819380,"http://example.com/is_root":true}
]")
jwt.set_algorithm_to_hs256
tok := jwt.encoded_string ("my-secret")
create l_loader
if
attached l_loader.token (tok, Void, "my-secret", Void) as l_tok and then
not l_tok.has_error
then
print (l_tok.claimset.string)
check verified: not l_tok.has_unverified_token_error end
check no_error: not l_tok.has_error end
end
end
test_jwt_io
local
jwt: JWS
@@ -185,7 +209,8 @@ feature -- Test
tok := jwt.encoded_string ("secret")
if attached (create {JWT_LOADER}).token (tok, "HS256", "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("error", l_tok.has_error)
assert ("has_mismatched_alg_error", l_tok.has_mismatched_alg_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
end
@@ -205,15 +230,50 @@ feature -- Test
tok := jwt.encoded_string ("secret")
if attached (create {JWT_LOADER}).token (tok, "none", "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
if attached (create {JWT_LOADER}).token (tok, Void, "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
end
test_additional_alg
local
jwt: JWS
payload: STRING
tok: STRING
l_loader: JWT_LOADER
do
payload := "[
{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}
]"
create jwt.make_with_json_payload (payload)
jwt.algorithms.register_algorithm (create {JWT_ALG_TEST})
jwt.set_algorithm ({JWT_ALG_TEST}.name)
tok := jwt.encoded_string ("secret")
create l_loader
l_loader.algorithms.register_algorithm (create {JWT_ALG_TEST})
if attached l_loader.token (tok, "test", "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error)
assert ("not has_unsupported_alg_error", not l_tok.has_unsupported_alg_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
if attached l_loader.token (tok, Void, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
create l_loader
if attached l_loader.token (tok, "test", "secret", Void) as l_tok then
assert ("has error", l_tok.has_error)
assert ("has_unsupported_alg_error", l_tok.has_unsupported_alg_error)
end
end
feature -- Implementation
duplicated_time (dt: DATE_TIME): DATE_TIME

View File

@@ -38,11 +38,11 @@ feature -- Execution
exec.execute
res.push
exec.clean
elseif exec /= Void then
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
exec.clean
else
process_rescue (res)
if exec /= Void then
exec.clean
end
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
end
rescue
if not rescued then
@@ -51,24 +51,6 @@ feature -- Execution
end
end
process_rescue (res: detachable WGI_RESPONSE)
-- Handle rescued execution of current request.
do
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
end
if res.message_writable then
res.put_string ("<pre>")
res.put_string (html_encoder.encoded_string (l_trace))
res.put_string ("</pre>")
end
res.push
end
end
end
note
copyright: "2011-2015, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -73,11 +73,11 @@ feature -- Execution
exec.execute
res.push
exec.clean
elseif exec /= Void then
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
exec.clean
else
process_rescue (res)
if exec /= Void then
exec.clean
end
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
end
rescue
if not rescued then
@@ -86,24 +86,6 @@ feature -- Execution
end
end
process_rescue (res: detachable WGI_RESPONSE)
-- Handle rescued execution of current request.
do
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
end
if res.message_writable then
res.put_string ("<pre>")
res.put_string (html_encoder.encoded_string (l_trace))
res.put_string ("</pre>")
end
res.push
end
end
end
feature -- Input/Output
input: WGI_LIBFCGI_INPUT_STREAM

View File

@@ -88,13 +88,15 @@ feature -- Request processing
exec.execute
res.push
exec.clean
else
elseif exec /= Void then
if not has_error then
process_rescue (res)
end
if exec /= Void then
exec.clean
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
end
exec.clean
elseif not has_error then
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
else
-- Bad error.
end
rescue
if l_output = Void or else not l_output.is_available then
@@ -106,31 +108,6 @@ feature -- Request processing
end
end
process_rescue (res: detachable WGI_RESPONSE)
local
s: STRING
do
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
if res /= Void then
if not res.status_is_set then
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
end
create s.make_empty
s.append ("<pre>")
s.append (html_encoder.encoded_string (l_trace))
s.append ("</pre>")
if not res.header_committed then
res.put_header_text ("Content-Type: text/html%R%NContent-Length: " + s.count.out + "%R%N%R%N")
end
if res.message_writable then
res.put_string (s)
end
res.push
end
end
end
httpd_environment (a_socket: HTTPD_STREAM_SOCKET): STRING_TABLE [READABLE_STRING_8]
local
p: INTEGER

View File

@@ -110,7 +110,9 @@ feature -- Header output operation
if
not l_connection.is_case_insensitive_equal_general ("close")
then
s.replace_substring ("Connection: close", i + 1, j - 1)
if not l_connection.is_case_insensitive_equal_general ("upgrade") then
s.replace_substring ("Connection: close", i + 1, j - 1)
end
end
elseif not is_http_version_1_0 then
-- HTTP/1.1: always return "close" since persistent connection is not supported.

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-18-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-18-0 http://www.eiffel.com/developers/xml/configuration-1-18-0.xsd" name="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
<target name="ewsgi_spec">
<root all_classes="true"/>
<file_rule>
@@ -10,6 +10,8 @@
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="specification" location="specification\" recursive="true"/>
</target>

View File

@@ -42,6 +42,14 @@ feature -- Execution
is_valid_end_of_execution: is_valid_end_of_execution
end
feature {WGI_EXPORTER, WGI_CONNECTOR} -- Execution: rescue
execute_rescue (e: detachable EXCEPTION)
-- Execute on rescue.
do
(create {WGI_RESCUE_EXECUTION}).execute (request, response, e)
end
feature -- Status report
is_valid_end_of_execution: BOOLEAN

View File

@@ -0,0 +1,56 @@
note
description: "Execution when an exception occurred."
date: "$Date$"
revision: "$Revision$"
class
WGI_RESCUE_EXECUTION
inherit
WGI_EXPORTER
SHARED_HTML_ENCODER
feature -- Execution
execute (req: detachable WGI_REQUEST; res: detachable WGI_RESPONSE; e: detachable EXCEPTION)
-- Exception or internal error occurred, return the eventual trace
-- as response.
-- `req` and `res` may be available for processing.
local
s: STRING
do
if
res /= Void and then
e /= Void and then
attached e.trace as l_trace
then
if not res.status_is_set then
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
end
create s.make_empty
s.append ("<pre>")
s.append (html_encoder.encoded_string (l_trace))
s.append ("</pre>")
if not res.header_committed then
-- Overwrite any header previously set.
res.put_header_text ("Content-Type: text/html%R%NContent-Length: " + s.count.out + "%R%N%R%N")
end
if res.message_writable then
res.put_string (s)
end
res.push
end
end
note
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -11,6 +11,8 @@ inherit
HTTPD_CONSTANTS
SOCKET_TIMEOUT_UTILITIES
feature {NONE} -- Initialization
make
@@ -18,9 +20,9 @@ feature {NONE} -- Initialization
http_server_port := default_http_server_port
max_concurrent_connections := default_max_concurrent_connections
max_tcp_clients := default_max_tcp_clients
socket_timeout := default_socket_timeout
socket_recv_timeout := default_socket_recv_timeout
keep_alive_timeout := default_keep_alive_timeout
socket_timeout_ns := seconds_to_nanoseconds (default_socket_timeout)
socket_recv_timeout_ns := seconds_to_nanoseconds (default_socket_recv_timeout)
keep_alive_timeout_ns := seconds_to_nanoseconds (default_keep_alive_timeout)
max_keep_alive_requests := default_max_keep_alive_requests
is_secure := False
create secure_certificate.make_empty
@@ -39,12 +41,12 @@ feature -- Access
max_tcp_clients: INTEGER assign set_max_tcp_clients
-- Listen on socket for at most `queue' connections.
socket_timeout: INTEGER assign set_socket_timeout
socket_timeout_ns: NATURAL_64 assign set_socket_timeout_ns
-- Amount of seconds that the server waits for receipts and transmissions during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: 60 seconds, which is appropriate for most situations.
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
socket_recv_timeout_ns: NATURAL_64 assign set_socket_recv_timeout_ns
-- Amount of seconds that the server waits for receiving data during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: 5 seconds.
@@ -65,7 +67,7 @@ feature -- Access
verbose_level: INTEGER assign set_verbose_level
-- Verbosity of output.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
keep_alive_timeout_ns: NATURAL_64 assign set_keep_alive_timeout_ns
-- Persistent connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Timeout unit in Seconds.
@@ -86,9 +88,9 @@ feature -- Access
do
Result.is_verbose := is_verbose
Result.verbose_level := verbose_level
Result.timeout := socket_timeout
Result.socket_recv_timeout := socket_recv_timeout
Result.keep_alive_timeout := keep_alive_timeout
Result.timeout_ns := socket_timeout_ns
Result.socket_recv_timeout_ns := socket_recv_timeout_ns
Result.keep_alive_timeout_ns := keep_alive_timeout_ns
Result.max_keep_alive_requests := max_keep_alive_requests
Result.is_secure := is_secure
end
@@ -166,28 +168,52 @@ feature -- Element change
max_concurrent_connections_set : max_concurrent_connections = v
end
set_socket_timeout (a_nb_seconds: like socket_timeout)
set_socket_timeout_ns (a_nano_seconds: like socket_timeout_ns)
-- Set `socket_timeout_ns' with `a_nano_seconds'.
require
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
do
socket_timeout_ns := a_nano_seconds
ensure
socket_timeout_ns_set: socket_timeout_ns = a_nano_seconds
end
set_socket_recv_timeout_ns (a_nano_seconds: like socket_recv_timeout_ns)
-- Set `socket_recv_timeout_ns' with `a_nano_seconds'.
require
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
do
socket_recv_timeout_ns := a_nano_seconds
ensure
socket_recv_timeout_ns_set: socket_recv_timeout_ns = a_nano_seconds
end
set_keep_alive_timeout_ns (a_nano_seconds: like keep_alive_timeout_ns)
-- Set `keep_alive_timeout_ns' with `a_nano_seconds'.
require
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
do
keep_alive_timeout_ns := a_nano_seconds
ensure
keep_alive_timeout_ns_set: keep_alive_timeout_ns = a_nano_seconds
end
set_socket_timeout (a_nb_seconds: INTEGER)
-- Set `socket_timeout' with `a_nb_seconds'.
do
socket_timeout := a_nb_seconds
ensure
socket_timeout_set: socket_timeout = a_nb_seconds
set_socket_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
end
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
set_socket_recv_timeout (a_nb_seconds: INTEGER)
-- Set `socket_recv_timeout' with `a_nb_seconds'.
do
socket_recv_timeout := a_nb_seconds
ensure
socket_recv_timeout_set: socket_recv_timeout = a_nb_seconds
set_socket_recv_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
end
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds'.
set_keep_alive_timeout (a_nb_seconds: INTEGER)
-- Set `keep_alive_timeout' with `a_nb_seconds'.
do
keep_alive_timeout := a_seconds
ensure
keep_alive_timeout_set: keep_alive_timeout = a_seconds
set_keep_alive_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
@@ -296,6 +322,8 @@ feature -- Element change
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `secure_protocol' with `a_ssl_version'.
local
err: DEVELOPER_EXCEPTION
do
if a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
set_secure_protocol_to_tls_1_2
@@ -307,7 +335,9 @@ feature -- Element change
set_secure_protocol_to_dtls_1_0
elseif a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
-- Obsolete!
set_secure_protocol_to_ssl_2_or_3
create err
err.set_description ("SSL_2 or SSL_3 are not supported anymore, upgrate to TLS set_secure_protocol_to_tls_1_2")
err.raise
else -- Default
set_secure_protocol_to_tls_1_2
end
@@ -343,7 +373,7 @@ feature -- SSL Helpers
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -9,6 +9,9 @@ note
expanded class
HTTPD_REQUEST_SETTINGS
inherit
SOCKET_TIMEOUT_UTILITIES
feature -- Access
is_verbose: BOOLEAN assign set_is_verbose
@@ -20,13 +23,13 @@ feature -- Access
is_secure: BOOLEAN assign set_is_secure
-- Is using secure connection? i.e SSL?
timeout: INTEGER assign set_timeout
timeout_ns: NATURAL_64 assign set_timeout_ns
-- Amount of seconds that the server waits for receipts and transmissions during communications.
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
socket_recv_timeout_ns: NATURAL_64 assign set_socket_recv_timeout_ns
-- Amount of seconds that the server waits for receiving data on socket during communications.
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
keep_alive_timeout_ns: NATURAL_64 assign set_keep_alive_timeout_ns
-- Keep-alive timeout, also known as persistent-connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Unit in Seconds.
@@ -34,6 +37,29 @@ feature -- Access
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
feature -- Access: obsolete
timeout: INTEGER assign set_timeout
obsolete
"Use `timeout_ns` [2018-10-29]"
do
Result := nanoseconds_to_seconds (timeout_ns)
end
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
obsolete
"Use `socket_recv_timeout_ns` [2018-10-29]"
do
Result := nanoseconds_to_seconds (socket_recv_timeout_ns)
end
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
obsolete
"Use `keep_alive_timeout_ns` [2018-10-29]"
do
Result := nanoseconds_to_seconds (keep_alive_timeout_ns)
end
feature -- Change
set_is_verbose (b: BOOLEAN)
@@ -54,24 +80,48 @@ feature -- Change
is_secure := b
end
feature -- Timeout change
set_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
-- Set `timeout_ns' to `a_timeout_in_nanoseconds'.
do
timeout_ns := a_timeout_in_nanoseconds
end
set_socket_recv_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
-- Set `socket_recv_timeout_ns' to `a_timeout_in_nanoseconds'.
do
socket_recv_timeout_ns := a_timeout_in_nanoseconds
end
set_keep_alive_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
-- Set `keep_alive_timeout_ns' to `a_timeout_in_nanoseconds'.
do
keep_alive_timeout_ns := a_timeout_in_nanoseconds
end
feature -- Timeout change (in seconds)
set_timeout (a_timeout_in_seconds: INTEGER)
-- Set `timeout' to `a_timeout_in_seconds'.
do
timeout := a_timeout_in_seconds
set_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
end
set_socket_recv_timeout (a_timeout_in_seconds: INTEGER)
-- Set `socket_recv_timeout' to `a_timeout_in_seconds'.
do
socket_recv_timeout := a_timeout_in_seconds
set_socket_recv_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
end
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
do
keep_alive_timeout := a_timeout_in_seconds
set_keep_alive_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
end
feature -- Change
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
@@ -79,7 +129,7 @@ feature -- Change
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -19,9 +19,9 @@ feature {NONE} -- Initialization
do
reset
-- Import global request settings.
timeout := a_request_settings.timeout -- seconds
socket_recv_timeout := a_request_settings.socket_recv_timeout -- seconds
keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds
timeout_ns := a_request_settings.timeout_ns -- nanoseconds
socket_recv_timeout_ns := a_request_settings.socket_recv_timeout_ns -- nanoseconds
keep_alive_timeout_ns := a_request_settings.keep_alive_timeout_ns -- nanoseconds
max_keep_alive_requests := a_request_settings.max_keep_alive_requests
is_verbose := a_request_settings.is_verbose
@@ -148,17 +148,17 @@ feature -- Settings
-- Is next persistent connection supported?
-- note: it is relevant only if `is_persistent_connection_supported' is True.
timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receipts and transmissions during communications.
timeout_ns: NATURAL_64 -- nanoseconds
-- Amount of nanoseconds that the server waits for receipts and transmissions during communications.
socket_recv_timeout: INTEGER -- seconds
-- Amount of seconds that the server waits for receiving data on socket during communications.
socket_recv_timeout_ns: NATURAL_64 -- nanoseconds
-- Amount of nanoseconds that the server waits for receiving data on socket during communications.
max_keep_alive_requests: INTEGER
-- Maximum number of requests allowed per persistent connection.
keep_alive_timeout: INTEGER -- seconds
-- Number of seconds for persistent connection timeout.
keep_alive_timeout_ns: NATURAL_64 -- nanoseconds
-- Number of nanoseconds for persistent connection timeout.
feature -- Status report
@@ -226,6 +226,10 @@ feature -- Execution
do
l_socket := client_socket
-- Set to expected `timeout_ns`.
l_socket.set_timeout_ns (timeout_ns)
l_socket.set_recv_timeout_ns (socket_recv_timeout_ns)
-- Compute remote info once for the persistent connection.
create l_remote_info
if attached l_socket.peer_address as l_addr then
@@ -299,13 +303,13 @@ feature -- Execution
end
-- Try to get request header.
-- If the request is reusing persistent connection, use `keep_alive_timeout',
-- otherwise `socket_recv_timeout'.
-- If the request is reusing persistent connection, use `keep_alive_timeout_ns',
-- otherwise `socket_recv_timeout_ns'.
get_request_header (l_socket, a_is_reusing_connection)
if has_error then
if a_is_reusing_connection and then request_header.is_empty then
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'.
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout_ns'.
debug ("dbglog")
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
end
@@ -407,9 +411,9 @@ feature -- Parsing
a_socket.readable
then
if a_is_reusing_connection then
a_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
a_socket.set_recv_timeout_ns (keep_alive_timeout_ns) -- in nanoseconds!
else
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
a_socket.set_recv_timeout_ns (socket_recv_timeout_ns) -- FIXME: return a 408 Request Timeout response ..
end
if
@@ -424,7 +428,7 @@ feature -- Parsing
if not has_error then
if a_is_reusing_connection then
-- Restore normal recv timeout!
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
a_socket.set_recv_timeout_ns (socket_recv_timeout_ns) -- FIXME: return a 408 Request Timeout response ..
end
from
line := next_line (a_socket)
@@ -646,7 +650,7 @@ invariant
request_header_attached: request_header /= Void
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -109,9 +109,9 @@ feature -- Execution
log (" - port = " + configuration.http_server_port.out)
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
log (" - socket_recv_timeout = " + configuration.socket_recv_timeout.out + " seconds")
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
log (" - socket_timeout = " + timeout_representation (configuration.socket_timeout_ns))
log (" - socket_recv_timeout = " + timeout_representation (configuration.socket_recv_timeout_ns))
log (" - keep_alive_timeout = " + timeout_representation (configuration.keep_alive_timeout_ns))
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
if configuration.has_secure_support then
if configuration.is_secure then
@@ -366,8 +366,25 @@ feature -- Output
end
end
timeout_representation (a_ns: NATURAL_64): STRING
do
if 1_000 * (a_ns // 1_000) = a_ns then
if 1_000_000 * (a_ns // 1_000_000) = a_ns then
if 1_000_000_000 * (a_ns // 1_000_000_000) = a_ns then
Result := (a_ns // 1_000_000_000).out + " seconds"
else
Result := (a_ns // 1_000_000).out + " milliseconds"
end
else
Result := (a_ns // 1_000).out + " microseconds"
end
else
Result := a_ns.out + " nanoseconds"
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -13,7 +13,8 @@ CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp
CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp
link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\msc
mkdir /S %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\%ISE_C_COMPILER%
endlocal
exit 0
rem exit 0

View File

@@ -13,7 +13,8 @@ CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp
CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp
link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\msc
mkdir /S %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
endlocal
exit 0
rem exit 0

View File

@@ -12,10 +12,10 @@ note
max_concurrent_connections: set to 1, for single threaded behavior
max_tcp_clients: max number of open tcp connection
socket_timeout: connection timeout
socket_recv_timeout: read data timeout
socket_timeout_ns: connection timeout in nanoseconds
socket_recv_timeout_ns: read data timeout in nanoseconds
keep_alive_timeout: amount of time the server will wait for subsequent
keep_alive_timeout_ns: amount of nanoseconds the server will wait for subsequent
requests on a persistent connection,
max_keep_alive_requests: number of requests allowed on a persistent connection,
@@ -40,6 +40,8 @@ inherit
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
WSF_TIMEOUT_UTILITIES
create
make,
make_and_launch
@@ -57,9 +59,9 @@ feature {NONE} -- Initialization
port_number := {WGI_STANDALONE_CONSTANTS}.default_http_server_port --| Default, but quite often, this port is already used ...
max_concurrent_connections := {WGI_STANDALONE_CONSTANTS}.default_max_concurrent_connections
max_tcp_clients := {WGI_STANDALONE_CONSTANTS}.default_max_tcp_clients
socket_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_timeout -- seconds
socket_recv_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout -- seconds
keep_alive_timeout := {WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout -- seconds.
socket_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_socket_timeout) -- default in seconds
socket_recv_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout) -- default in seconds
keep_alive_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout) -- default in seconds.
max_keep_alive_requests := {WGI_STANDALONE_CONSTANTS}.default_max_keep_alive_requests
verbose := False
verbose_level := notice_level
@@ -111,9 +113,9 @@ feature {NONE} -- Initialization
end
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections)
max_tcp_clients := opts.option_integer_value ("max_tcp_clients", max_tcp_clients)
socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout)
socket_recv_timeout := opts.option_integer_value ("socket_recv_timeout", socket_recv_timeout)
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
socket_timeout_ns := opts.option_timeout_ns_value ("socket_timeout", socket_timeout_ns)
socket_recv_timeout_ns := opts.option_timeout_ns_value ("socket_recv_timeout", socket_recv_timeout_ns)
keep_alive_timeout_ns := opts.option_timeout_ns_value ("keep_alive_timeout", keep_alive_timeout_ns)
max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
if
@@ -169,9 +171,9 @@ feature -- Execution
cfg.http_server_port := port_number
cfg.set_max_concurrent_connections (max_concurrent_connections)
cfg.set_max_tcp_clients (max_tcp_clients)
cfg.set_socket_timeout (socket_timeout)
cfg.set_socket_recv_timeout (socket_recv_timeout)
cfg.set_keep_alive_timeout (keep_alive_timeout)
cfg.set_socket_timeout_ns (socket_timeout_ns)
cfg.set_socket_recv_timeout_ns (socket_recv_timeout_ns)
cfg.set_keep_alive_timeout_ns (keep_alive_timeout_ns)
cfg.set_max_keep_alive_requests (max_keep_alive_requests)
end
@@ -244,10 +246,10 @@ feature {NONE} -- Implementation
end
max_tcp_clients: INTEGER
socket_timeout: INTEGER
socket_recv_timeout: INTEGER
socket_timeout_ns: NATURAL_64
socket_recv_timeout_ns: NATURAL_64
keep_alive_timeout: INTEGER
keep_alive_timeout_ns: NATURAL_64
max_keep_alive_requests: INTEGER
is_secure_connection_supported: BOOLEAN

View File

@@ -23,6 +23,8 @@ deferred class
inherit
WEB_SOCKET_CONSTANTS
WSF_TIMEOUT_UTILITIES
REFACTORING_HELPER
feature -- Web Socket Interface
@@ -90,13 +92,18 @@ feature -- Websocket events
feature {WEB_SOCKET} -- Timeout.
timer_delay: INTEGER
-- Maximal duration in seconds between two `on_timeout` event.
timer_delay_ns: NATURAL_64
-- Maximal duration in nanoseconds between two `on_timeout` event.
-- Disable timeout event, by setting it to `0` (default).
set_timer_delay_ns (nb_nanosecs: NATURAL_64)
do
timer_delay_ns := nb_nanosecs
end
set_timer_delay (nb_secs: INTEGER)
do
timer_delay := nb_secs
timer_delay_ns := seconds_to_nanoseconds (nb_secs)
end
on_timer (ws: WEB_SOCKET)
@@ -143,7 +150,7 @@ feature -- Websocket events: implemented
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -42,7 +42,7 @@ feature {NONE} -- Initialization
end
end
feature {NONE} -- Access
feature {WEB_SOCKET_EVENT_I} -- Access
request: WSF_REQUEST
-- Associated request.
@@ -209,29 +209,30 @@ feature {WEB_SOCKET_HANDLER} -- Networking
wait_for_input (cb: detachable WEB_SOCKET_EVENT_I)
local
l_timeout, nb: INTEGER
l_cb_timeout: INTEGER
nb,
l_timeout_ns,
l_cb_timeout_ns: NATURAL_64
do
has_input := False
if cb = Void then
has_input := socket.ready_for_reading
else
l_cb_timeout := cb.timer_delay
l_timeout := socket.timeout
if l_cb_timeout = 0 then
l_cb_timeout_ns := cb.timer_delay_ns
l_timeout_ns := socket.timeout_ns
if l_cb_timeout_ns = 0 then
-- timeout event not enabled.
has_input := socket.ready_for_reading
else
cb.on_timer (Current)
if l_cb_timeout > l_timeout then
if l_cb_timeout_ns > l_timeout_ns then
-- event timeout duration is bigger than socket timeout
-- thus, no on_timeout before next frame waiting
has_input := socket.ready_for_reading
else
from
l_timeout := socket.timeout
nb := l_timeout
socket.set_timeout (l_cb_timeout) -- FIXME: for now 1 sec is the smaller timeout we can use.
l_timeout_ns := socket.timeout_ns
nb := l_timeout_ns
socket.set_timeout_ns (l_cb_timeout_ns)
until
has_input or nb <= 0
loop
@@ -239,13 +240,13 @@ feature {WEB_SOCKET_HANDLER} -- Networking
if not has_input then
-- Call on_timeout only if there is no input,
-- otherwise it was called once before the initial wait.
socket.set_timeout (l_timeout)
socket.set_timeout_ns (l_timeout_ns)
cb.on_timer (Current)
socket.set_timeout (l_cb_timeout)
socket.set_timeout_ns (l_cb_timeout_ns)
end
nb := nb - l_cb_timeout
nb := nb - l_cb_timeout_ns
end
socket.set_timeout (l_timeout)
socket.set_timeout_ns (l_timeout_ns)
end
end
end
@@ -772,7 +773,7 @@ feature {NONE} -- Debug
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -1,8 +1,11 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
description: "[
Handler that can also play the role of a filter, i.e.
than can pre-process incoming data and post-process outgoing data.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_EXECUTE_FILTER_HANDLER
@@ -22,7 +25,7 @@ feature -- Execution
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -8,10 +8,19 @@ deferred class
WSF_URI_FILTER_HANDLER
inherit
WSF_EXECUTE_FILTER_HANDLER
WSF_FILTER_HANDLER [WSF_URI_HANDLER]
WSF_URI_HANDLER
feature -- Execution
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
do
if attached next as n then
n.execute (req, res)
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -11,7 +11,9 @@ inherit
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
WSF_URI_RESPONSE_HANDLER
undefine
execute
end
create
make

View File

@@ -22,6 +22,14 @@ feature -- Response
Result_attached: Result /= Void
end
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
res.send (response (req))
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -8,10 +8,19 @@ deferred class
WSF_URI_TEMPLATE_FILTER_HANDLER
inherit
WSF_EXECUTE_FILTER_HANDLER
WSF_FILTER_HANDLER [WSF_URI_TEMPLATE_HANDLER]
WSF_URI_TEMPLATE_HANDLER
feature -- Execution
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
do
if attached next as n then
n.execute (req, res)
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -11,6 +11,9 @@ inherit
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
WSF_URI_TEMPLATE_RESPONSE_HANDLER
undefine
execute
end
create
make

View File

@@ -22,6 +22,14 @@ feature -- Response
Result_attached: Result /= Void
end
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
res.send (response (req))
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,36 @@
note
description: "[
{WSF_XSS_FILTER}.
Simple anti cross-site scripting (XSS) filter.
Remove all suspicious strings from request parameters (query strings and form) before returning them to the application
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_XSS_FILTER
inherit
WSF_FILTER
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
do
execute_next (create {WSF_XSS_REQUEST}.make_from_request (req), res)
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,94 @@
note
description: "[
Security protection on values.
It could be to protect against XSS, SQL ... injections.
]"
date: "$Date$"
revision: "$Revision$"
EIS: "name=OWASP", "src=https://www.owasp.org/", "protocol=uri"
EIS: "name=OWASP XSS", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
deferred class
WSF_PROTECTION
feature -- Status report
is_valid: BOOLEAN
-- Is valid protection?
deferred
end
feature -- String Protection
string_8 (s: READABLE_STRING_8): detachable READABLE_STRING_8
-- Safe string value from `s`.
-- If a thread is detected, either return Void, or filter out the threat.
require
is_valid: is_valid
deferred
end
feature -- Value Protection
string_value (v: WSF_STRING): detachable WSF_STRING
-- Safe string value from `v`.
-- If a thread is detected, either return Void, or filter out the threat.
require
is_valid: is_valid
deferred
end
value (v: WSF_VALUE): detachable WSF_VALUE
-- Safe value from `v`.
-- If a thread is detected, either return Void, or filter out the threat.
require
is_valid: is_valid
do
if attached {WSF_STRING} v as s then
Result := string_value (s)
elseif attached {WSF_MULTIPLE_STRING} v as ms then
Result := multiple_string_value (ms)
else
-- TODO
Result := v
end
end
multiple_string_value (mv: WSF_MULTIPLE_STRING): detachable WSF_MULTIPLE_STRING
-- Safe multiple string value from `mv`.
-- If a thread is detected in any of the item, either return Void, or filter out the threat.
require
is_valid: is_valid
local
v: detachable WSF_STRING
do
-- TODO: check if the whole structure should be Void
-- when one item is filtered out, or if the structure could have
-- holes.
across
mv as ic
loop
v := string_value (ic.item)
if v = Void then
Result := Void
elseif Result = Void then
create Result.make_with_value (v)
else
Result.add_value (v)
end
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,489 @@
note
description: "Return data for WSF_REQUEST query and form parameters using different types of protection policy"
date: "$Date$"
revision: "$Revision$"
class
WSF_PROTECTION_POLICY
-- TODO add header protection.
feature -- Query parameters
custom_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with custom protections.
do
Result := custom_wsf_value (a_req.query_parameter (a_name), a_protections)
end
predefined_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with all predefined protections.
-- check {WSF_PROTECTIONS} class.
do
Result := predefined_value (a_req.query_parameter (a_name))
end
xss_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with xss protection.
do
Result := xss_value (a_req.query_parameter (a_name))
end
xss_js_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with xss protection.
do
Result := xss_js_value (a_req.query_parameter (a_name))
end
sql_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with sql injection protection.
do
Result := sql_value (a_req.query_parameter (a_name))
end
server_side_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with server side injection protection.
do
Result := server_side_value (a_req.query_parameter (a_name))
end
xpath_abbreviated_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with XPath_abbreviated injection protection.
do
Result := xpath_abbreviated_value (a_req.query_parameter (a_name))
end
xpath_expanded_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Query parameter name `a_name' with XPath expanded injection protection.
do
Result := xpath_expanded_value (a_req.query_parameter (a_name))
end
feature -- Form Parameters
custom_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with custom protections.
do
Result := custom_wsf_value (a_req.form_parameter (a_name), a_protections)
end
predefined_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with all predefined protections.
-- check {WSF_PROTECTIONS} class.
do
Result := predefined_value (a_req.form_parameter (a_name))
end
xss_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with xss protection.
do
Result := xss_value (a_req.form_parameter (a_name))
end
xss_js_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with xss protection.
do
Result := xss_js_value (a_req.form_parameter (a_name))
end
sql_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with sql injection protection.
do
Result := sql_value (a_req.form_parameter (a_name))
end
server_side_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with server side injection protection.
do
Result := server_side_value (a_req.form_parameter (a_name))
end
xpath_abbreviated_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with server Xpath abbreviated injection protection.
do
Result := xpath_abbreviated_value (a_req.form_parameter (a_name))
end
xpath_expanded_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered Form parameter name `a_name' with server Xpath expanded injection protection.
do
Result := xpath_expanded_value (a_req.form_parameter (a_name))
end
feature -- Meta Variables
custom_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
-- Filtered CGI Meta variable name `a_name' with custom protections.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} custom_wsf_value (a_req.meta_variable (a_name), a_protections) as l_result then
Result := l_result
end
end
predefined_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Filtered CGI Meta variable name `a_name' with predefined protections.
-- check {WSF_PROTECTIONS} class.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} predefined_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
xss_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with xss protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} xss_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
xss_js_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with xss protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} xss_js_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
sql_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with sql injection protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} sql_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
server_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with server side injection protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} server_side_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
xpath_abbreviated_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} xpath_abbreviated_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
xpath_expanded_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection.
require
a_name_valid: a_name /= Void and then not a_name.is_empty
do
if attached {WSF_STRING} xpath_expanded_value (a_req.meta_variable (a_name)) as l_result then
Result := l_result
end
end
feature -- HTTP_*
custom_http_accept (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_accept header with custom protections `a_protections`.
-- Contents of the Accept: header from the current wgi_request, if there is one.
-- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
do
Result := custom_string_value (a_req.http_accept, a_protections)
end
custom_http_accept_charset (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_accept_charset header with custom protections `a_protections`.
-- Contents of the Accept-Charset: header from the current wgi_request, if there is one.
-- Example: 'iso-8859-1,*,utf-8'.
do
Result := custom_string_value (a_req.http_accept_charset, a_protections)
end
custom_http_accept_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_accept_encoding header with custom protections `a_protections`.
-- Contents of the Accept-Encoding: header from the current wgi_request, if there is one.
-- Example: 'gzip'.
do
Result := custom_string_value (a_req.http_accept_encoding, a_protections)
end
custom_http_accept_language (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_accept_language header with custom protections `a_protections`.
-- Contents of the Accept-Language: header from the current wgi_request, if there is one.
-- Example: 'en'.
do
Result := custom_string_value (a_req.http_accept_language, a_protections)
end
custom_http_connection (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_connection header with custom protections `a_protections`.
-- Contents of the Connection: header from the current wgi_request, if there is one.
-- Example: 'keep-alive'.
do
Result := custom_string_value (a_req.http_connection, a_protections)
end
custom_http_expect (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_expect header with custom protections `a_protections`.
-- The Expect request-header field is used to indicate that particular server behaviors are required by the client.
-- Example: '100-continue'.
do
Result := custom_string_value (a_req.http_expect, a_protections)
end
custom_http_host (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_host header with custom protections `a_protections`.
-- Contents of the Host: header from the current wgi_request, if there is one.
do
Result := custom_string_value (a_req.http_host, a_protections)
end
custom_http_referer (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_referer header with custom protections `a_protections`.
-- The address of the page (if any) which referred the user agent to the current page.
-- This is set by the user agent.
-- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature.
-- In short, it cannot really be trusted.
do
Result := custom_string_value (a_req.http_referer, a_protections)
end
custom_http_user_agent (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_user_agent header with custom protections `a_protections`.
-- Contents of the User-Agent: header from the current wgi_request, if there is one.
-- This is a string denoting the user agent being which is accessing the page.
-- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
-- Among other things, you can use this value to tailor your page's
-- output to the capabilities of the user agent.
do
Result := custom_string_value (a_req.http_user_agent, a_protections)
end
custom_http_authorization (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_authorization header with custom protections `a_protections`.
-- Contents of the Authorization: header from the current wgi_request, if there is one.
do
Result := custom_string_value (a_req.http_authorization, a_protections)
end
custom_http_transfer_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_transfer_encoding header with custom protections `a_protections`.
-- Transfer-Encoding
-- for instance chunked.
do
Result := custom_string_value (a_req.http_transfer_encoding, a_protections)
end
custom_http_access_control_request_headers (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_access_control_request_headers header with custom protections `a_protections`.
-- Indicates which headers will be used in the actual request
-- as part of the preflight request
do
Result := custom_string_value (a_req.http_access_control_request_headers, a_protections)
end
custom_http_if_match (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_if_match header with custom protections `a_protections`.
-- Existence check on resource.
do
Result := custom_string_value (a_req.http_if_match, a_protections)
end
custom_http_if_modified_since (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_if_modified_since header with custom protections `a_protections`.
-- Modification check on resource.
do
Result := custom_string_value (a_req.http_if_modified_since, a_protections)
end
custom_http_if_none_match (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_if_none_match header with custom protections `a_protections`.
-- Existence check on resource.
do
Result := custom_string_value (a_req.http_if_none_match, a_protections)
end
custom_http_if_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_if_range header with custom protections `a_protections`.
-- Existence check on resource.
do
Result := custom_string_value (a_req.http_if_range, a_protections)
end
custom_http_if_unmodified_since (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_if_unmodified_since header with custom protections `a_protections`.
-- Modification check on resource.
do
Result := custom_string_value (a_req.http_if_unmodified_since, a_protections)
end
custom_http_last_modified (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_last_modified header with custom protections `a_protections`.
-- Modification check on resource.
do
Result := custom_string_value (a_req.http_last_modified, a_protections)
end
custom_http_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_range header with custom protections `a_protections`.
-- Requested byte-range of resource.
do
Result := custom_string_value (a_req.http_range, a_protections)
end
custom_http_content_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_content_range header with custom protections `a_protections`.
-- Partial range of selected representation enclosed in message payload.
do
Result := custom_string_value (a_req.http_content_range, a_protections)
end
custom_http_content_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Filtered http_content_encoding header with custom protections `a_protections`.
-- Encoding (usually compression) of message payload.
do
Result := custom_string_value (a_req.http_content_encoding, a_protections)
end
feature {NONE} -- Implementation
custom_wsf_value (a_value: detachable WSF_VALUE; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
-- Return value `a_value` filtered by all protections policy.
do
Result := filter_wsf_value (a_value, a_protections )
end
custom_string_value (a_value: detachable READABLE_STRING_8; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
-- Return value `a_value` filtered by all protections policy.
do
Result := filter_string_value (a_value, a_protections )
end
predefined_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by all predefined protections policy.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value,
<<
l_wsf_xss.XSS,
l_wsf_xss.server_side,
l_wsf_xss.sql_injection,
l_wsf_xss.xpath_abbreviated,
l_wsf_xss.xpath_expanded
>>)
end
xss_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by xss protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.XSS>>)
end
xss_js_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by xss-javascript protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.XSS_javascript>>)
end
sql_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by sql injection protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.SQL_injection>>)
end
server_side_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by server side injection protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.Server_side>>)
end
xpath_abbreviated_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by xpath_abbreviated injection protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.Xpath_abbreviated>>)
end
xpath_expanded_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
-- Return value `a_value` filtered by Xpath expanded injection protection.
local
l_wsf_xss: WSF_PROTECTIONS
do
Result := filter_wsf_value (a_value, <<l_wsf_xss.Xpath_expanded>>)
end
filter_wsf_value (a_value: detachable WSF_VALUE; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
-- Filter value `a_value` with an array of protections policy `a_protections`.
require
a_protections_valid: across a_protections as ic all ic.item.is_valid end
local
prot: WSF_PROTECTION
do
if a_value /= Void then
Result := a_value
across
a_protections as ic
until
Result = Void
loop
prot := ic.item
check is_valid: prot.is_valid end
Result := prot.value (Result)
end
end
end
filter_string_value (a_value: detachable READABLE_STRING_8; a_protections: ITERABLE [WSF_PROTECTION] ): detachable READABLE_STRING_8
-- Filter value `a_value` with an array of protections policy `a_protections`.
require
all_protections_valid: across a_protections as ic all ic.item.is_valid end
local
prot: WSF_PROTECTION
do
if a_value /= Void then
Result := a_value
across
a_protections as ic
until
Result = Void
loop
prot := ic.item
check is_valid: prot.is_valid end
Result := prot.string_8 (Result)
end
end
end
note
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,114 @@
note
description: "Security protection based on Regular expression."
date: "$Date$"
revision: "$Revision$"
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
class
WSF_PROTECTION_REGEXP
inherit
WSF_PROTECTION
create
make,
make_caseless,
make_with_regexp
convert
make_with_regexp ({REGULAR_EXPRESSION})
feature {NONE} -- Initialization
make (a_regexp_pattern: READABLE_STRING_8; a_caseless: BOOLEAN)
local
r: REGULAR_EXPRESSION
do
create r
r.set_caseless (a_caseless)
r.compile (a_regexp_pattern)
make_with_regexp (r)
end
make_caseless (a_regexp_pattern: READABLE_STRING_8)
do
make (a_regexp_pattern, True)
end
make_with_regexp (a_regexp: REGULAR_EXPRESSION)
do
regexp := a_regexp
end
feature -- Access
regexp: REGULAR_EXPRESSION
feature -- String Protection
string_8 (s: READABLE_STRING_8): detachable READABLE_STRING_8
local
reg: like regexp
do
reg := regexp
reg.match (s)
if reg.has_matched then
Result := Void
else
Result := s
end
end
string_value (v: WSF_STRING): detachable WSF_STRING
local
vs: READABLE_STRING_8
do
vs := v.url_encoded_value
if attached string_8 (vs) as s then
if vs = s then
Result := v
else
create Result.make (v.name, s)
end
end
end
feature -- Status report
is_valid: BOOLEAN
-- <Precursor>
-- i.e: if the association regular expression is successfully compiled.
do
Result := is_compiled
end
is_compiled: BOOLEAN
do
Result := regexp.is_compiled
end
feature {NONE} -- Implementation
compiled_regexp (p: STRING; caseless: BOOLEAN): REGULAR_EXPRESSION
require
p /= Void
do
create Result
Result.set_caseless (caseless)
Result.compile (p)
ensure
is_compiled: Result.is_compiled
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,100 @@
note
description: "[
{WSF_PROTECTIONS}
Provide application security parterns to assist in Cross Site Scripting
]"
date: "$Date$"
revision: "$Revision$"
EIS: "name=OWASP XSS", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
expanded class
WSF_PROTECTIONS
feature -- XSS patterns
XSS: WSF_PROTECTION_REGEXP
note
EIS: "name= XSS", "src=https://community.apigee.com/questions/27198/xss-threat-protection-patterns.html#answer-27465", "protocol=uri"
once
create Result.make_caseless ("((\%%3C)|<)[^\n]+((\%%3E)|>)")
ensure
is_compiled: Result.is_compiled
end
XSS_javascript: WSF_PROTECTION_REGEXP
note
EIS: "name=JavaScript Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
once
Result := compiled_regexp ("<\s*script\b[^>]*>[^<]+<\s*/\s*script\s*>", True)
ensure
is_compiled: Result.is_compiled
end
feature -- XPath injections Patterns
XPath_abbreviated: WSF_PROTECTION_REGEXP
note
EIS: "name=XPath Abbreviated Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
once
Result := compiled_regexp ("(/(@?[\w_?\w:\*]+(\[[^]]+\])*)?)+", True)
ensure
is_compiled: Result.is_compiled
end
XPath_expanded: WSF_PROTECTION_REGEXP
note
EIS: "name=XPath Expanded Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
once
Result := compiled_regexp ("/?(ancestor(-or-self)?|descendant(-or-self)?|following(-sibling))", True)
ensure
is_compiled: Result.is_compiled
end
feature -- Server side injection
Server_side: WSF_PROTECTION_REGEXP
note
EIS: "name=Server-Side Include Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
once
Result := compiled_regexp ("<!--#(include|exec|echo|config|printenv)\s+.*", True)
ensure
is_compiled: Result.is_compiled
end
feature -- SQL injection Patterns
SQL_injection: WSF_PROTECTION_REGEXP
note
EIS: "name= SQL Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
once
Result := compiled_regexp ("[\s]*((delete)|(exec)|(drop\s*table)|(insert)|(shutdown)|(update)|(\bor\b))", True)
ensure
is_compiled: Result.is_compiled
end
feature {NONE} -- Implementation
compiled_regexp (p: STRING; caseless: BOOLEAN): REGULAR_EXPRESSION
require
p /= Void
do
create Result
Result.set_caseless (caseless)
Result.compile (p)
ensure
is_compiled: Result.is_compiled
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,260 @@
note
description: "[
XSS request, redefine query_parameter and form_parameters filtering the data (using XSS protection)
before return the value.
The XSS protection pattern used is defined here :{WSF_PROTECTIONS}.XSS: WSF_PROTECTION
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_XSS_REQUEST
inherit
WSF_REQUEST
redefine
query_parameter,
form_parameter,
meta_variable,
http_accept,
http_accept_charset,
http_accept_encoding,
http_accept_language,
http_connection,
http_expect,
http_host,
http_referer,
http_user_agent,
http_authorization,
http_transfer_encoding,
http_access_control_request_headers,
http_if_match,
http_if_modified_since,
http_if_none_match,
http_if_range,
http_if_unmodified_since,
http_last_modified,
http_range,
http_content_range,
http_content_encoding
end
WSF_REQUEST_EXPORTER
WSF_PROTECTION_POLICY
create
make_from_request
feature {NONE} -- Creation
make_from_request (req: WSF_REQUEST)
do
make_from_wgi (req.wgi_request)
end
feature -- Query parameters
query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- <Precursor>
do
Result := xss_query_parameter (Current, a_name)
end
feature -- Form Parameters
form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- <Precursor>
do
Result := xss_form_parameter (Current, a_name)
end
feature -- Meta Variable
meta_variable (a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
-- <Precursor>
do
Result := xss_meta_variable (Current, a_name)
end
feature -- HTTP_*
http_accept: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_accept (Current, <<l_protection.xss>>)
end
http_accept_charset: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_accept_charset (Current, <<l_protection.xss>>)
end
http_accept_encoding: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_accept_encoding (Current, <<l_protection.xss>>)
end
http_accept_language: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_accept_language (Current, <<l_protection.xss>>)
end
http_connection: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_connection (Current, <<l_protection.xss>>)
end
http_expect: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_expect (Current, <<l_protection.xss>>)
end
http_host: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_host (Current, <<l_protection.xss>>)
end
http_referer: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_referer (Current, <<l_protection.xss>>)
end
http_user_agent: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_user_agent (Current, <<l_protection.xss>>)
end
http_authorization: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_authorization (Current, <<l_protection.xss>>)
end
http_transfer_encoding: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_transfer_encoding (Current, <<l_protection.xss>>)
end
http_access_control_request_headers: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_access_control_request_headers (Current, <<l_protection.xss>>)
end
http_if_match: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_if_match (Current, <<l_protection.xss>>)
end
http_if_modified_since: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_if_modified_since (Current, <<l_protection.xss>>)
end
http_if_none_match: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_if_none_match (Current, <<l_protection.xss>>)
end
http_if_range: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_if_range (Current, <<l_protection.xss>>)
end
http_if_unmodified_since: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_if_unmodified_since (Current, <<l_protection.xss>>)
end
http_last_modified: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_last_modified (Current, <<l_protection.xss>>)
end
http_range: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_range (Current, <<l_protection.xss>>)
end
http_content_range: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_content_range (Current, <<l_protection.xss>>)
end
http_content_encoding: detachable READABLE_STRING_8
-- <Precursor>
local
l_protection: WSF_PROTECTIONS
do
Result := custom_http_content_encoding (Current, <<l_protection.xss>>)
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -1,6 +1,6 @@
note
description : "Objects that ..."
author : "$Author$"
author : "$Author: jfiat $"
date : "$Date$"
revision : "$Revision$"
@@ -51,7 +51,6 @@ feature {NONE} -- Initialization
create h.make
header := h
h.put_content_type (content_type)
h.put_transfer_encoding_binary
h.put_content_length (filesize (file_path))
h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"")
if attached filedate (file_path) as dt then
@@ -215,22 +214,14 @@ feature -- Implementation: output
create f.make_with_path (fn)
check f.exists and then f.is_readable end
f.open_read
from
until
f.exhausted
loop
f.read_stream (4_096)
res.put_string (f.last_string)
end
f.close
res.put_file_content (f, 0, f.count)
end
invariant
status_code_set: status_code /= 0
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -152,6 +152,80 @@ feature -- Helpers
end
end
option_natural_64_value (a_opt_name: READABLE_STRING_GENERAL; a_default: NATURAL_64): NATURAL_64
-- NATURAL_64 value associated to option name `a_opt_name', other return `a_default'.
local
s: READABLE_STRING_GENERAL
do
Result := a_default
if attached option (a_opt_name) as opt then
if attached {NATURAL_64} opt as n then
Result := n
else
s := opt.out
if s.is_natural_64 then
Result := s.to_natural_64
end
end
end
end
option_timeout_ns_value (a_opt_name: READABLE_STRING_GENERAL; a_default: NATURAL_64): NATURAL_64
-- Timeout in nanoseconds associated to option name `a_opt_name', other return `a_default'.
local
v: detachable READABLE_STRING_GENERAL
s: STRING_32
i,l_count: INTEGER
do
if a_opt_name.ends_with ("_ns") then
-- Option use "nanoseconds" convention.
Result := option_natural_64_value (a_opt_name, a_default)
elseif attached option (a_opt_name) as opt then
if attached {READABLE_STRING_GENERAL} opt as str then
from
i := 1
l_count := str.count
until
i > l_count or v /= Void
loop
if str[i].is_space then
-- ignore
elseif str[i].is_digit then
-- Keep it
else
v := str.head (i - 1)
create s.make_from_string_general (str.substring (i, l_count))
s.adjust
end
i := i + 1
end
if v = Void then
v := str
end
if v /= Void then
if s = Void or else s.is_whitespace or else s.is_case_insensitive_equal_general ("s") then
-- Consider as `seconds` for backward compatibility
Result := timeout_utilities.seconds_to_nanoseconds (v.to_integer)
elseif s.is_case_insensitive_equal_general ("ns") then
check v.is_natural_64 end
Result := v.to_natural_64
elseif s.is_case_insensitive_equal_general ("us") then
check v.is_integer end
Result := timeout_utilities.microseconds_to_nanoseconds (v.to_integer)
elseif s.is_case_insensitive_equal_general ("ms") then
check v.is_integer end
Result := timeout_utilities.milliseconds_to_nanoseconds (v.to_integer)
end
else
-- Error!
end
else
-- Backward comptability with timeout in seconds!
Result := timeout_utilities.seconds_to_nanoseconds (option_integer_value (a_opt_name, timeout_utilities.nanoseconds_to_seconds (a_default)))
end
end
end
option_boolean_value (a_opt_name: READABLE_STRING_GENERAL; a_default: BOOLEAN): BOOLEAN
-- BOOLEAN value associated to option name `a_opt_name', other return `a_default'.
local
@@ -222,6 +296,11 @@ feature {NONE} -- Implementation
options: STRING_TABLE [detachable ANY]
-- Custom options which might be support (or not) by the default service
timeout_utilities: WSF_TIMEOUT_UTILITIES
once
create Result
end
invariant
options_attached: options /= Void
note

View File

@@ -0,0 +1,93 @@
note
description: "Utility for timeout conversion."
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
seealso: "See also {SOCKET_TIMEOUT_UTILITIES} from EiffelNet library."
class
WSF_TIMEOUT_UTILITIES
feature -- Time helpers
one_second_in_nanoseconds: NATURAL_64 = 1_000_000_000
-- Number of nanoseconds in one second.
one_millisecond_in_nanoseconds: NATURAL_64 = 1_000_000
-- Number of nanoseconds in one millisecond.
one_microsecond_in_nanoseconds: NATURAL_64 = 1_000
-- Number of nanoseconds in one microsecond.
max_timeout_ns_value: NATURAL_64 = 2_147_483_647_999_999_999
-- Maximum value for a timeout in nanoseconds.
-- See `is_valid_timeout_ns` for explanation.
nanoseconds_to_seconds (ns: NATURAL_64): INTEGER
-- Number of seconds in `ns` nanoseconds (that fits in INTEGER_32 value).
-- Warning: Result can not be more than {INTEGER}.max_value.
local
n64: NATURAL_64
do
n64 := ns // one_second_in_nanoseconds
if n64 > {INTEGER}.max_value.to_natural_64 then
Result := {INTEGER}.max_value
else
Result := n64.to_integer_32
end
ensure
non_negative: Result >= 0
coherent: Result.to_natural_64 * one_second_in_nanoseconds <= ns
is_class: class
end
seconds_to_nanoseconds (s: INTEGER): NATURAL_64
-- Number of nanoseconds in `s` seconds (that fits in NATURAL_64 value).
do
Result := one_second_in_nanoseconds * s.to_natural_64
ensure
is_class: class
end
milliseconds_to_nanoseconds (ms: INTEGER): NATURAL_64
-- Number of nanoseconds in `ms` milliseconds (that fits in NATURAL_64 value).
do
Result := one_millisecond_in_nanoseconds * ms.to_natural_64
ensure
is_class: class
end
microseconds_to_nanoseconds (us: INTEGER): NATURAL_64
-- Number of nanoseconds in `us` microseconds (that fits in NATURAL_64 value).
do
Result := one_microsecond_in_nanoseconds * us.to_natural_64
ensure
is_class: class
end
feature -- Validation
is_valid_timeout_ns (ns: NATURAL_64): BOOLEAN
--| Note: internally (C API), the ns timeout is splitted into:
--| - a long value `tv_sec` : number of seconds
--| - a long value `tv_usec`: number of microseconds (with struct timeval)
--| or in the future
--| - a long value `tv_nsec`: number of nanoseconds (with struct timespec)
--| tv_usec or tv_nsec should be >= 0 and < equivalent of 1 second.
--| tv_usec < 1_000_000
--| tv_nsec < 1_000_000_000
--| so there is no issue with NATURAL_64 for usec or nsec
--| but, then, there is restriction for the seconds value which is coded as long int
--| and long values (32 bits) are between -2_147_483_647 and +2_147_483_647 (i.e 2e31 - 1 = {INTEGER_32}.max_value).
--| then, the `seconds` part of the timeout <= +2_147_483_647, and then expressed in nanoseconds, and taking into account
--| the usec or nsec part, the timeout should not be greater than:
--| +2_147_483_647 * 1_000_000_000 + 999_999_999 = 2_147_483_647_999_999_999
--| and as NATURAL_64's max value is bigger = 18_446_744_073_709_551_615
--| features dealing with timeout in nanoseconds have related preconditions.
do
Result := 0 <= ns and then ns <= max_timeout_ns_value
ensure
is_class: class
end
end

View File

@@ -0,0 +1,129 @@
note
description: "Summary description for {TEST_WSF_PROTECTION_POLICY}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
TEST_WSF_PROTECTION_POLICY
inherit
EQA_TEST_SET
WSF_SERVICE
undefine
default_create
end
feature -- Test
test_http_expect_attack_without_xss_protection
local
req: WSF_REQUEST
do
--| Case HTTP header expect attack, no filtered.
req := new_request (<<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/cookie"],
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
>>
)
-- no filter
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", attached req.http_expect as v and then v.is_case_insensitive_equal ("<script>alert(XSS attack)</script>"))
end
test_http_expect_attack_with_xss_protection
local
req: WSF_REQUEST
sec: WSF_PROTECTION_POLICY
l_protection: WSF_PROTECTIONS
do
create sec
--| Case HTTP header expect attack, filtered using {xss_regular_expression}
req := new_request (<<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/xss_example"],
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
>>
)
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", sec.custom_http_expect (req, <<l_protection.xss>>) = Void)
end
test_http_expect_attack_with_xss_js_protection
local
req: WSF_REQUEST
sec: WSF_PROTECTION_POLICY
l_protection: WSF_PROTECTIONS
do
create sec
--| Case HTTP header expect attack, filtered using {xss_javascript_expression}
req := new_request (<<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/xss_example"],
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
>>
)
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", sec.custom_http_expect (req, <<l_protection.xss_javascript>>) = Void )
end
test_http_referer_attack_with_xss_js_protection_fails
local
req: WSF_REQUEST
sec: WSF_PROTECTION_POLICY
l_protection: WSF_PROTECTIONS
l_str: STRING
do
l_str:= "[
Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5
]"
create sec
--| Case HTTP header referer attack, filtered using {xss_javascript_expression}
req := new_request (<<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/xss_example"],
["HTTP_REFERER", l_str]
>>
)
assert ("HTTP_REFERER", attached sec.custom_http_referer (req, <<l_protection.xss_javascript>>) as v and then not v.is_empty )
end
test_http_referer_attack_with_xss_protection
local
req: WSF_REQUEST
sec: WSF_PROTECTION_POLICY
l_protection: WSF_PROTECTIONS
l_str: STRING
do
l_str:= "[
Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5
]"
create sec
--| Case HTTP header referer attack, filtered using {xss_javascript_expression}
req := new_request (<<
["REQUEST_METHOD", "GET"],
["QUERY_STRING", ""],
["REQUEST_URI", "/xss_example"],
["HTTP_REFERER", l_str]
>>
)
assert ("HTTP_REFERER", attached {READABLE_STRING_8} sec.custom_http_referer (req, <<l_protection.xss>>) as v and then not v.is_empty )
end
feature {NONE} -- Implementation
new_request (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]): WSF_REQUEST_NULL
local
wgi_req: WGI_REQUEST
do
create {WGI_REQUEST_NULL} wgi_req.make_with_file (a_meta, io.input)
create Result.make_from_wgi (wgi_req)
end
end

View File

@@ -0,0 +1,252 @@
note
description: "Summary description for {TEST_XSS_PATTERNS}."
date: "$Date$"
revision: "$Revision$"
EIS: "name=XSS Filter Evasion Cheat Sheet", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
class
TEST_XSS_PATTERNS
inherit
EQA_TEST_SET
feature -- Tests
test_xss_locator
local
xss: WSF_XSS_REQUEST
r: REGULAR_EXPRESSION
s: STRING
do
s:= "[
';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";
alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--
></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("XSS locator", r.has_matched)
end
test_xss_locator_short
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
'';!--"<XSS>=&{()}
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("XSS locator short", r.has_matched)
end
test_no_filter_evasion
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("No filter evasion", r.has_matched)
end
test_filter_bypass_based_polyglot
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)>
<script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script>
<script>alert(document.cookie)</script>">
<img/id="confirm&lpar;1)"/alt="/"src="/"onerror=eval(id)>'">
<img src="http://www.shellypalmer.com/wp-content/images/2015/07/hacked-compressor.jpg">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Filter bypass based polyglot", r.has_matched)
end
test_image_xss_js_directive
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC="javascript:alert('XSS');">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Image XSS using the JavaScript directive", r.has_matched)
end
test_no_quotes_no_semicolon
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=javascript:alert('XSS')>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("No quotes and no semicolon", r.has_matched)
end
test_case_insensitive_xss_vector
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=JaVaScRiPt:alert('XSS')>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Case insensitive XSS attack vector", r.has_matched)
end
test_html_entities
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=javascript:alert(&quot;XSS&quot;)>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("HTML entities", r.has_matched)
end
test_grave_accent_obfuscation
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Grave accent obfuscation", r.has_matched)
end
test_malformed_a_tags
local
r: REGULAR_EXPRESSION
s: STRING
do
-- Skip the HREF attribute and get to the meat of the XXS... Submitted by David Cross ~ Verified on Chrome
s:="[
<a onmouseover="alert(document.cookie)">xxs link</a>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Malformed A tags", r.has_matched)
end
test_malformed_a_tags_2
local
r: REGULAR_EXPRESSION
s: STRING
do
-- Chrome loves to replace missing quotes for you... if you ever get stuck just leave them off and Chrome will put them
-- in the right place and fix your missing quotes on a URL or script.
s:="[
<a onmouseover=alert(document.cookie)>xxs link</a>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Malformed A tags", r.has_matched)
end
test_malformed_img
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Malformed IMG tags", r.has_matched)
end
test_from_char_code
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("fromCharCode", r.has_matched)
end
test_default_src_tag
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC=# onmouseover="alert('xxs')">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Default SRC tag to get past filters that check SRC domain", r.has_matched)
end
test_default_src_tag_2
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG SRC= onmouseover="alert('xxs')">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Default SRC tag by leaving it empty", r.has_matched)
end
test_default_src_tag_3
local
r: REGULAR_EXPRESSION
s: STRING
do
s:="[
<IMG onmouseover="alert('xxs')">
]"
r:= xss_pattern.XSS.regexp
r.match (s)
assert ("Default SRC tag by leaving it out entirely", r.has_matched)
end
feature {NONE} -- Implementation
xss_pattern: WSF_PROTECTIONS
end

View File

@@ -3,9 +3,9 @@
<target name="server">
<root class="TEST" feature="make"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option debug="false" warning="true">
<assertions precondition="true" postcondition="true" check="true" loop="true" supplier_precondition="true"/>
@@ -21,12 +21,14 @@
</library>
<library name="http" location="..\..\..\network\protocol\http\http.ecf" readonly="false"/>
<library name="http_client" location="..\..\..\network\http_client\net_http_client.ecf" readonly="false"/>
<library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="..\wsf.ecf" readonly="false">
<option>
<assertions precondition="true" postcondition="true" check="true" supplier_precondition="true"/>
</option>
</library>
<library name="wsf_security" location="..\wsf_security.ecf" readonly="false"/>
<library name="wsf_standalone" location="..\..\wsf\connector\standalone.ecf" readonly="false"/>
<cluster name="server" location=".\server\" recursive="true"/>
</target>

View File

@@ -3,9 +3,9 @@
<target name="wsf">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="wsf_security" uuid="6684959A-6F63-4861-A98E-7E144AE77F2E" library_target="wsf_security">
<target name="wsf_security">
<root all_classes="true"/>
<file_rule>
<exclude>/EIFGENs$</exclude>
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
<library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/>
<library name="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/>
<library name="wsf" location="wsf.ecf"/>
<library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/>
<cluster name="security" location=".\security\" recursive="true"/>
</target>
</system>

View File

@@ -111,9 +111,9 @@ feature -- Execution
i := l_raw_header.substring_index ("%R%N", 1)
if i > 0 then
-- Skip the first status line.
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count).as_string_8_conversion) -- NOTE: it is string 8 per nature.
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count).to_string_8) -- NOTE: it is string 8 per nature.
else
create h.make_from_raw_header_data (l_raw_header.as_string_8_conversion)
create h.make_from_raw_header_data (l_raw_header.to_string_8)
end
if attached l_remote_uri.host as l_remote_host then
if l_remote_uri.port > 0 then

View File

@@ -48,6 +48,7 @@ feature -- Encoder
if uc.is_character_8 then
c := uc.to_character_8
inspect c
when '%U' then Result.append_string ("\u0000")
when '%"' then Result.append_string ("\%"")
when '\' then Result.append_string ("\\")
when '%B' then Result.append_string ("\b")

View File

@@ -39,12 +39,16 @@
<library name="wsf_cgi" location="..\library\server\wsf\connector\cgi.ecf" readonly="false"/>
<library name="wsf_extension" location="..\library\server\wsf\wsf_extension.ecf" readonly="false"/>
<library name="wsf_html" location="..\library\server\wsf_html\wsf_html.ecf" readonly="false"/>
<library name="wsf_proxy" location="..\library\server\wsf_proxy\wsf_proxy.ecf" readonly="false"/>
<library name="wsf_js_widget" location="..\draft\library\server\wsf_js_widget\wsf_js_widget.ecf" readonly="false"/>
<library name="wsf_standalone" location="..\library\server\wsf\connector\standalone.ecf" readonly="false"/>
<library name="wsf_standalone_websocket" location="..\library\server\wsf\connector\standalone_websocket.ecf" readonly="false"/>
<library name="wsf_libfcgi" location="..\library\server\wsf\connector\libfcgi.ecf" readonly="false"/>
<library name="wsf_openshift" location="..\library\server\wsf\connector\openshift.ecf" readonly="false"/>
<library name="wsf_policy_driven" location="..\library\server\wsf\wsf_policy_driven.ecf" readonly="false"/>
<library name="wsf_router_context" location="..\library\server\wsf\wsf_router_context.ecf" readonly="false"/>
<library name="wsf_session" location="..\library\server\wsf\wsf_session.ecf" readonly="false"/>
<library name="wsf_security" location="..\library\server\wsf\wsf_security.ecf" readonly="false"/>
</target>
<target name="all_none" extends="all">
<description>Compiling with None concurrency</description>

View File

@@ -65,6 +65,8 @@ echo Install library: ewf/wsf_proxy
%COPYCMD% %TMP_DIR%\library\server\wsf_proxy %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_proxy
echo Install library: ewf/wsf_html
%COPYCMD% %TMP_DIR%\library\server\wsf_html %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_html
echo Install library: ewf/wsf_security
%COPYCMD% %TMP_DIR%\library\server\wsf_security %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_security
echo Install library: ewf/encoder
%COPYCMD% %TMP_DIR%\library\text\encoder %TMP_CONTRIB_DIR%\library\web\framework\ewf\text\encoder

View File

@@ -46,7 +46,7 @@ echo Uninstall framework: ewf
echo Uninstall ewf examples
%RDCMD% %TMP_CONTRIB_DIR%\examples\web\ewf
%RDCMD% %TMP_CONTRIB_DIR%\examples\ewb\ewf_precomp
%RDCMD% %TMP_CONTRIB_DIR%\examples\web\ewf_precomp
echo Uninstall ewf wizard
%RDCMD% %TMP_TARGET_DIR%\help\wizards\ewf