Skip to content

Commit 356d1ec

Browse files
committed
test: command pipe permissions, error-body sanitisation, TLS minimum
Self-contained cram tests (POSIX sh, no bash helper) for the security hardening: - security.t: the command pipe is created 0o600 (F8); a handler error returns a generic body, never the OCaml exception (F4). - security-tls.t: an HTTPS listener accepts TLS 1.2 and refuses TLS 1.1 (F5).
1 parent 894ccb5 commit 356d1ec

8 files changed

Lines changed: 99 additions & 0 deletions

File tree

test/security-tls.t/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(executable
2+
(name test)
3+
(libraries ocsigenserver))

test/security-tls.t/dune-project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(lang dune 3.18)

test/security-tls.t/run.t

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
F5: the HTTPS server must require TLS 1.2 at minimum (TLS 1.0/1.1 deprecated).
2+
Self-contained POSIX sh.
3+
4+
$ mkdir -p log data
5+
$ openssl req -x509 -newkey rsa:2048 -nodes -keyout privkey.pem \
6+
> -out cert.pem -days 2 -subj /CN=localhost >/dev/null 2>&1
7+
$ dune build ./test.exe 2>&1
8+
$ dune exec -- ./test.exe >server.log 2>&1 &
9+
$ trap 'echo shutdown > local.cmd 2>/dev/null; wait' EXIT
10+
$ i=0; while [ ! -e local.cmd ]; do
11+
> i=$((i+1)); [ $i -gt 200 ] && break; sleep 0.05; done
12+
$ i=0; while ! curl -sk --tls-max 1.2 https://127.0.0.1:8453/ >/dev/null 2>&1; do
13+
> i=$((i+1)); [ $i -gt 200 ] && break; sleep 0.05; done
14+
15+
A TLS 1.2 client succeeds:
16+
17+
$ curl -sk --tlsv1.2 --tls-max 1.2 -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8453/
18+
200
19+
20+
A TLS 1.1 client is refused (curl returns a non-zero exit code):
21+
22+
$ curl -sk --tls-max 1.1 https://127.0.0.1:8453/ >/dev/null 2>&1 && echo accepted || echo rejected
23+
rejected

test/security-tls.t/test.ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(* F5: the HTTPS server must require TLS 1.2 at minimum. This starts an HTTPS
2+
listener with a self-signed certificate (generated by the cram test). The
3+
handler is irrelevant: the test only checks the TLS handshake. *)
4+
5+
let respond _vh _config_info _path _request_state =
6+
Lwt.return
7+
(Ocsigen.Extensions.Ext_found
8+
(fun () ->
9+
Lwt.return
10+
(Ocsigen.Response.make
11+
(Cohttp.Response.make ~status:`OK ()))))
12+
13+
let () =
14+
Ocsigen.Server.start ~ports:[]
15+
~ssl_ports:[ (`All, 8453) ]
16+
~ssl_info:
17+
(Some
18+
{ Ocsigen.Config.ssl_certificate = Some "cert.pem"
19+
; ssl_privatekey = Some "privkey.pem"
20+
; ssl_ciphers = None
21+
; ssl_dhfile = None
22+
; ssl_curve = None })
23+
~logdir:"log" ~datadir:"data" ~uploaddir:None ~usedefaulthostname:true
24+
~command_pipe:"local.cmd" ~default_charset:(Some "utf-8")
25+
[ Ocsigen.Server.host [ respond ] ]

test/security.t/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(executable
2+
(name test)
3+
(libraries ocsigenserver))

test/security.t/dune-project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(lang dune 3.18)

test/security.t/run.t

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Security hardening checks: command pipe permissions (F8) and error-body
2+
sanitisation (F4). Self-contained (POSIX sh, no bash helper) so it runs
3+
regardless of the cram shell.
4+
5+
$ mkdir -p log data
6+
$ dune build ./test.exe 2>&1
7+
$ dune exec -- ./test.exe >server.log 2>&1 &
8+
$ trap 'echo shutdown > local.cmd 2>/dev/null; wait' EXIT
9+
$ i=0; while [ ! -e local.sock ] || [ ! -e local.cmd ]; do
10+
> i=$((i+1)); [ $i -gt 200 ] && break; sleep 0.05; done
11+
12+
F8: the command pipe is created with mode 0o600, so that only the server's own
13+
user can send it control commands.
14+
15+
$ stat -c '%a' local.cmd
16+
600
17+
18+
F4: a handler error returns the generic HTTP status reason phrase, never the
19+
OCaml exception (the word "secret" from the exception must not appear).
20+
21+
$ curl --unix-socket local.sock -s -o /dev/null -w '%{http_code}\n' http://x/anything
22+
500
23+
$ curl --unix-socket local.sock -s http://x/anything
24+
Error: Internal Server Error
25+
$ curl --unix-socket local.sock -s http://x/anything | grep -c secret || true
26+
0

test/security.t/test.ml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(* End-to-end checks for the security hardening:
2+
- F4: a handler error must not leak the OCaml exception in the response
3+
body; the client gets the generic HTTP status reason phrase.
4+
- F8: the command pipe must be created with mode 0o600.
5+
6+
The [boom] instruction intercepts every request and fails with an
7+
exception carrying a "secret" message that must never reach the client. *)
8+
9+
let boom _vh _config_info _path _request_state =
10+
Lwt.fail (Failure "secret internal detail that must not leak to the client")
11+
12+
let () =
13+
Ocsigen.Server.start
14+
~ports:[ (`Unix "./local.sock", 0) ]
15+
~logdir:"log" ~datadir:"data" ~uploaddir:None ~usedefaulthostname:true
16+
~command_pipe:"local.cmd" ~default_charset:(Some "utf-8")
17+
[ Ocsigen.Server.host [ boom ] ]

0 commit comments

Comments
 (0)