Merge branch 'master' into v1

This commit is contained in:
Jocelyn Fiat
2017-09-21 21:22:38 +02:00
15 changed files with 227 additions and 53 deletions

View File

@@ -0,0 +1,19 @@
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
- echo `ec -version`
- cd $current_dir
- echo Check projects compilation status...
branches:
only:
- master
- v1
script: compile_all -ecb -melt -list_failures -log_verbose -clean -options dotnet=false
group: stable
os: linux

View File

@@ -16,8 +16,19 @@ feature -- Access
deferred deferred
end end
get (a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
Result := new_session (a_url).get ("", ctx)
end
custom (a_method: READABLE_STRING_8; a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- Response for `a_method' request based on `a_url' and optional `ctx'.
do
Result := new_session (a_url).custom (a_method, "", ctx)
end
note note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -31,7 +31,11 @@ feature {NONE} -- Initialization
-- This can be used to reset/reinitialize Current with new url -- This can be used to reset/reinitialize Current with new url
-- in the case of redirection. -- in the case of redirection.
do do
if a_url.starts_with ("http://") or a_url.starts_with ("http://") then
url := a_url url := a_url
else
url := session.url (a_url, Void)
end
headers := session.headers.twin headers := session.headers.twin
if ctx /= Void then if ctx /= Void then
context := ctx context := ctx

View File

@@ -113,6 +113,7 @@ feature -- Access
-- Get URL data -- Get URL data
l_is_https := url.starts_with_general ("https://") l_is_https := url.starts_with_general ("https://")
create l_uri.make_from_string (url) create l_uri.make_from_string (url)
check valid_url: l_uri.is_valid end
l_port := l_uri.port l_port := l_uri.port
if l_port = 0 then if l_port = 0 then
if l_is_https then if l_is_https then

View File

@@ -56,8 +56,15 @@ feature -- Secure connection Helpers
set_secure_protocol_to_ssl_2_or_3 set_secure_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'. -- Set `ssl_protocol' with `Ssl_23'.
-- Protocol not supported anymore.
obsolete
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
local
err: DEVELOPER_EXCEPTION
do do
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23) 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
end end
set_secure_protocol_to_tls_1_0 set_secure_protocol_to_tls_1_0
@@ -176,7 +183,14 @@ feature -- Output
end end
note note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat 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)" 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 end

View File

@@ -0,0 +1,36 @@
note
description: "Summary description for {JWT_MISMATCHED_ALG_ERROR}."
date: "$Date$"
revision: "$Revision$"
class
JWT_MISMATCHED_ALG_ERROR
inherit
JWT_ERROR
create
make
feature {NONE} -- Initialization
make (a_alg, a_header_alg: READABLE_STRING_8)
do
alg := a_alg
header_alg := a_header_alg
end
feature -- Access
alg: READABLE_STRING_8
header_alg: READABLE_STRING_8
id: STRING = "ALG_MISMATCH"
message: READABLE_STRING_8
do
Result := "Header alg [" + header_alg + "] does not match given alg [" + alg + "]!"
end
end

View File

@@ -59,6 +59,8 @@ feature -- Status report
do do
if attached claimset.issuer as iss then if attached claimset.issuer as iss then
Result := a_issuer = Void or else a_issuer.same_string (iss) Result := a_issuer = Void or else a_issuer.same_string (iss)
else
Result := a_issuer = Void
end end
end end
@@ -66,6 +68,8 @@ feature -- Status report
do do
if attached claimset.audience as aud then if attached claimset.audience as aud then
Result := a_audience = Void or else a_audience.same_string (aud) Result := a_audience = Void or else a_audience.same_string (aud)
else
Result := a_audience = Void
end end
end end
@@ -118,6 +122,11 @@ feature {JWT_UTILITIES} -- Error reporting
l_errors.extend (err) l_errors.extend (err)
end end
report_mismatched_alg_error (alg, a_header_alg: READABLE_STRING_8)
do
report_error (create {JWT_MISMATCHED_ALG_ERROR}.make (alg, a_header_alg))
end
report_unsupported_alg_error (alg: READABLE_STRING_8) report_unsupported_alg_error (alg: READABLE_STRING_8)
do do
report_error (create {JWT_UNSUPPORTED_ALG_ERROR}.make (alg)) report_error (create {JWT_UNSUPPORTED_ALG_ERROR}.make (alg))

View File

@@ -266,11 +266,10 @@ feature {NONE} -- Implementation
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8 base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
local local
hs256: HMAC_SHA256 ut: JWT_UTILITIES
do do
create hs256.make_ascii_key (a_secret) create ut
hs256.update_from_string (s) Result := ut.base64_hmacsha256 (s, a_secret)
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
end end
end end

View File

@@ -1,8 +1,8 @@
note note
description: "Summary description for {JWT_LOADER}." description: "Loader and verifier to JWT token."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
EIS: "name=Known Critical vulnerabilities in JWT libs", "protocol=URI", "src=https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"
class class
JWT_LOADER JWT_LOADER
@@ -12,9 +12,13 @@ inherit
feature -- Access feature -- Access
token (a_token_input: READABLE_STRING_8; a_secret: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT token (a_token_input: READABLE_STRING_8; a_alg: detachable READABLE_STRING_8; a_verification_key: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT
-- Decoded token from `a_token_input` given the secret `a_secret`, and optional context `ctx` -- Decoded token from `a_token_input` given the verification key `a_verification_key` and optional (but recommended) signature algorithm `a_alg`, and optional context `ctx`
-- used to specify eventual issuer and various parameters. -- used to specify eventual issuer and various parameters.
-- 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)
local local
jws: JWS jws: JWS
i,j,n: INTEGER i,j,n: INTEGER
@@ -29,20 +33,27 @@ feature -- Access
l_enc_payload := a_token_input.substring (i + 1, j - 1) l_enc_payload := a_token_input.substring (i + 1, j - 1)
l_signature := a_token_input.substring (j + 1, n) l_signature := a_token_input.substring (j + 1, n)
create jws.make_with_json_payload (base64url_decode (l_enc_payload)) create jws.make_with_json_payload (base64url_decode (l_enc_payload))
alg := signature_algorithm_from_encoded_header (l_enc_header) alg := signature_algorithm_from_encoded_header (l_enc_header)
jws.set_algorithm (alg) if a_alg /= Void then
if alg /= Void and then not alg.is_case_insensitive_equal_general (a_alg) then
jws.report_mismatched_alg_error (a_alg, alg)
else
alg := a_alg
end
else
if alg = Void then if alg = Void then
-- Use default -- Use default
alg := alg_hs256 alg := alg_hs256
end end
end
jws.set_algorithm (alg)
check alg_set: alg /= Void end check alg_set: alg /= Void end
if ctx = Void or else not ctx.validation_ignored then if ctx = Void or else not ctx.validation_ignored then
if not is_supporting_signature_algorithm (alg) then if not is_supporting_signature_algorithm (alg) then
jws.report_unsupported_alg_error (alg) jws.report_unsupported_alg_error (alg)
alg := alg_hs256 alg := alg_hs256
end end
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_secret, alg)) then if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg)) then
jws.report_unverified_token_error jws.report_unverified_token_error
end end
if if

View File

@@ -61,7 +61,33 @@ feature -- Encoding
do do
create hs256.make_ascii_key (a_secret) create hs256.make_ascii_key (a_secret)
hs256.update_from_string (s) hs256.update_from_string (s)
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest -- 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 end
feature -- Decoding feature -- Decoding

View File

@@ -54,7 +54,14 @@ feature -- Test
create jwt_loader create jwt_loader
if attached jwt_loader.token (tok, "secret", Void) as l_tok then -- Use header alg!
if attached jwt_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
-- Use given alg!
if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload)) assert ("same payload", l_tok.claimset.string.same_string (payload))
end end
@@ -96,21 +103,21 @@ feature -- Test
create jwt_loader create jwt_loader
-- Test with validation + exp -- Test with validation + exp
if attached jwt_loader.token (tok, "secret", Void) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload)) assert ("same payload", l_tok.claimset.string.same_string (payload))
end end
create ctx create ctx
ctx.set_time (now) ctx.set_time (now)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
end end
dt := duplicated_time (now) dt := duplicated_time (now)
dt.hour_add (5) dt.hour_add (5)
ctx.set_time (dt) ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("exp error", l_tok.has_error) assert ("exp error", l_tok.has_error)
end end
@@ -122,7 +129,7 @@ feature -- Test
tok := jwt.encoded_string ("secret") tok := jwt.encoded_string ("secret")
ctx.set_time (now) ctx.set_time (now)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has nbf error", l_tok.has_error) assert ("has nbf error", l_tok.has_error)
end end
@@ -130,7 +137,7 @@ feature -- Test
dt.second_add (15) dt.second_add (15)
ctx.set_time (dt) ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has nbf error", l_tok.has_error) assert ("has nbf error", l_tok.has_error)
end end
@@ -138,31 +145,51 @@ feature -- Test
dt.minute_add (45) dt.minute_add (45)
ctx.set_time (dt) ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
end end
-- Test Issuer -- Test Issuer
ctx.set_issuer ("urn:foobar") ctx.set_issuer ("urn:foobar")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has iss error", l_tok.has_error) assert ("has iss error", l_tok.has_error)
end end
ctx.set_issuer ("urn:foo") ctx.set_issuer ("urn:foo")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
end end
-- Test Audience -- Test Audience
ctx.set_audience ("urn:foobar") ctx.set_audience ("urn:foobar")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has aud error", l_tok.has_error) assert ("has aud error", l_tok.has_error)
end end
ctx.set_audience ("urn:foo") ctx.set_audience ("urn:foo")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error) assert ("no error", not l_tok.has_error)
end end
end end
test_mismatched_alg_jwt
local
jwt: JWS
payload: STRING
tok: STRING
do
payload := "[
{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}
]"
create jwt.make_with_json_payload (payload)
jwt.set_algorithm ("none")
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 ("same payload", l_tok.claimset.string.same_string (payload))
end
end
test_unsecured_jwt test_unsecured_jwt
local local
jwt: JWS jwt: JWS
@@ -177,7 +204,11 @@ feature -- Test
jwt.set_algorithm ("none") jwt.set_algorithm ("none")
tok := jwt.encoded_string ("secret") tok := jwt.encoded_string ("secret")
if attached (create {JWT_LOADER}).token (tok, "secret", Void) as l_tok then if attached (create {JWT_LOADER}).token (tok, "none", "secret", Void) as l_tok then
assert ("no error", not jwt.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 jwt.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload)) assert ("same payload", l_tok.claimset.string.same_string (payload))
end end

View File

@@ -76,13 +76,13 @@ feature -- Initialization
a_http_authorization /= Void implies http_authorization /= Void a_http_authorization /= Void implies http_authorization /= Void
end end
make_basic_auth (u: READABLE_STRING_32; p: READABLE_STRING_32) make_basic_auth (u: READABLE_STRING_GENERAL; p: READABLE_STRING_GENERAL)
-- Create a Basic authentication. -- Create a Basic authentication.
do do
make_custom_auth (u, p, Basic_auth_type) make_custom_auth (u, p, Basic_auth_type)
end end
make_custom_auth (u: READABLE_STRING_32; p: READABLE_STRING_32; a_type: READABLE_STRING_8) make_custom_auth (u: READABLE_STRING_GENERAL; p: READABLE_STRING_GENERAL; a_type: READABLE_STRING_8)
-- Create a custom `a_type' authentication. -- Create a custom `a_type' authentication.
require require
a_type_accepted: a_type.is_case_insensitive_equal (Basic_auth_type) a_type_accepted: a_type.is_case_insensitive_equal (Basic_auth_type)
@@ -90,15 +90,20 @@ feature -- Initialization
local local
t: STRING_8 t: STRING_8
utf: UTF_CONVERTER utf: UTF_CONVERTER
s: STRING_32
do do
login := u create login.make_from_string_general (u)
password := p create password.make_from_string_general (p)
create t.make_from_string (a_type) create t.make_from_string (a_type)
t.left_adjust; t.right_adjust t.left_adjust; t.right_adjust
type := t type := t
if t.is_case_insensitive_equal (Basic_auth_type) then if t.is_case_insensitive_equal (Basic_auth_type) then
type := Basic_auth_type type := Basic_auth_type
create http_authorization.make_from_string ("Basic " + (create {BASE64}).encoded_string (utf.string_32_to_utf_8_string_8 (u + {STRING_32} ":" + p))) create s.make_from_string_general (u)
s.extend (':')
s.append_string_general (p)
create http_authorization.make_from_string ("Basic " + (create {BASE64}).encoded_string (utf.string_32_to_utf_8_string_8 (s)))
elseif t.is_case_insensitive_equal (Digest_auth_type) then elseif t.is_case_insensitive_equal (Digest_auth_type) then
type := Digest_auth_type type := Digest_auth_type
to_implement ("HTTP Authorization %""+ t +"%", not yet implemented") to_implement ("HTTP Authorization %""+ t +"%", not yet implemented")
@@ -115,9 +120,9 @@ feature -- Access
type: READABLE_STRING_8 type: READABLE_STRING_8
login: detachable READABLE_STRING_32 login: detachable IMMUTABLE_STRING_32
password: detachable READABLE_STRING_32 password: detachable IMMUTABLE_STRING_32
feature -- Status report feature -- Status report

View File

@@ -167,7 +167,7 @@ feature -- Element change
end end
set_socket_timeout (a_nb_seconds: like socket_timeout) set_socket_timeout (a_nb_seconds: like socket_timeout)
-- Set `socket_timeout' with `a_nb_seconds' -- Set `socket_timeout' with `a_nb_seconds'.
do do
socket_timeout := a_nb_seconds socket_timeout := a_nb_seconds
ensure ensure
@@ -175,7 +175,7 @@ feature -- Element change
end end
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout) set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
-- Set `socket_recv_timeout' with `a_nb_seconds' -- Set `socket_recv_timeout' with `a_nb_seconds'.
do do
socket_recv_timeout := a_nb_seconds socket_recv_timeout := a_nb_seconds
ensure ensure
@@ -183,7 +183,7 @@ feature -- Element change
end end
set_keep_alive_timeout (a_seconds: like keep_alive_timeout) set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds' -- Set `keep_alive_timeout' with `a_seconds'.
do do
keep_alive_timeout := a_seconds keep_alive_timeout := a_seconds
ensure ensure
@@ -191,7 +191,7 @@ feature -- Element change
end end
set_max_keep_alive_requests (nb: like max_keep_alive_requests) set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb' -- Set `max_keep_alive_requests' with `nb'.
do do
max_keep_alive_requests := nb max_keep_alive_requests := nb
ensure ensure
@@ -254,7 +254,7 @@ feature -- Element change
end end
mark_secure mark_secure
-- Set is_secure in True -- Set is_secure in True.
do do
set_is_secure (True) set_is_secure (True)
ensure ensure
@@ -287,7 +287,7 @@ feature -- Element change
end end
set_secure_protocol (a_version: NATURAL) set_secure_protocol (a_version: NATURAL)
-- Set `secure_protocol' with `a_version' -- Set `secure_protocol' with `a_version'.
do do
secure_protocol := a_version secure_protocol := a_version
ensure ensure
@@ -295,7 +295,7 @@ feature -- Element change
end end
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL) set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `secure_protocol' with `a_ssl_version' -- Set `secure_protocol' with `a_ssl_version'.
do do
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
set_secure_protocol_to_ssl_2_or_3 set_secure_protocol_to_ssl_2_or_3
@@ -316,6 +316,8 @@ feature -- SSL Helpers
set_secure_protocol_to_ssl_2_or_3 set_secure_protocol_to_ssl_2_or_3
-- Set `secure_protocol' with `Ssl_23'. -- Set `secure_protocol' with `Ssl_23'.
obsolete
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
deferred deferred
end end

View File

@@ -36,9 +36,16 @@ feature -- Access
feature -- SSL Helpers feature -- SSL Helpers
set_secure_protocol_to_ssl_2_or_3 set_secure_protocol_to_ssl_2_or_3
-- Set `secure_protocol' with `Ssl_23'. -- Set `ssl_protocol' with `Ssl_23'.
-- Protocol not supported anymore.
obsolete
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
local
err: DEVELOPER_EXCEPTION
do do
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23) 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
end end
set_secure_protocol_to_tls_1_0 set_secure_protocol_to_tls_1_0
@@ -67,7 +74,7 @@ feature -- SSL Helpers
note note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[ source: "[
Eiffel Software Eiffel Software

View File

@@ -24,7 +24,6 @@
</option> </option>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/> <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
<cluster name="src" location="src\" recursive="true"/> <cluster name="src" location="src\" recursive="true"/>
<override name="override" location="override\" recursive="true"/>
</target> </target>
<target name="hello_cgi" extends="hello_dev"> <target name="hello_cgi" extends="hello_dev">
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/> <library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>