From d1889ba90e3c24a9b70bbd9f222bad5933ef56d0 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Fri, 8 Apr 2022 12:14:36 -0400 Subject: [PATCH 01/12] Add HSTS to F# ApiServer and BwdServer --- fsharp-backend/src/ApiServer/ApiServer.fs | 2 ++ fsharp-backend/src/BwdServer/Server.fs | 2 ++ fsharp-backend/src/LibService/HSTS.fs | 9 +++++++++ fsharp-backend/src/LibService/LibService.fsproj | 1 + fsharp-backend/tests/Tests/ApiServer.Tests.fs | 2 +- 5 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 fsharp-backend/src/LibService/HSTS.fs diff --git a/fsharp-backend/src/ApiServer/ApiServer.fs b/fsharp-backend/src/ApiServer/ApiServer.fs index c59a0bdaba..67ad02a893 100644 --- a/fsharp-backend/src/ApiServer/ApiServer.fs +++ b/fsharp-backend/src/ApiServer/ApiServer.fs @@ -172,6 +172,7 @@ let configureApp (packages : Packages) (appBuilder : WebApplication) = |> fun app -> LibService.Rollbar.AspNet.addRollbarToApp app rollbarCtxToMetadata None |> fun app -> app.UseHttpsRedirection() + |> fun app -> app.UseHsts() |> fun app -> app.UseRouting() // must go after UseRouting |> LibService.Kubernetes.configureApp LibService.Config.apiServerKubernetesPort @@ -189,6 +190,7 @@ let configureServices (services : IServiceCollection) : unit = LibService.Telemetry.TraceDBQueries |> LibService.Kubernetes.configureServices [ LibBackend.Init.legacyServerCheck ] |> fun s -> s.AddServerTiming() + |> fun s -> s.AddHsts (LibService.HSTS.setConfig) |> ignore let run (packages : Packages) : unit = diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index 8985f4214c..d68f8d92f9 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -480,6 +480,7 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = |> fun app -> app.UseRouting() // must go after UseRouting |> Kubernetes.configureApp healthCheckPort + |> fun app -> app.UseHsts() |> fun app -> app.Run(RequestDelegate handler) let configureServices (services : IServiceCollection) : unit = @@ -487,6 +488,7 @@ let configureServices (services : IServiceCollection) : unit = |> Kubernetes.configureServices [ LibBackend.Init.legacyServerCheck ] |> Rollbar.AspNet.addRollbarToServices |> Telemetry.AspNet.addTelemetryToServices "BwdServer" Telemetry.TraceDBQueries + |> fun s -> s.AddHsts (LibService.HSTS.setConfig) |> ignore let noLogger (builder : ILoggingBuilder) : unit = diff --git a/fsharp-backend/src/LibService/HSTS.fs b/fsharp-backend/src/LibService/HSTS.fs new file mode 100644 index 0000000000..574b5b98c3 --- /dev/null +++ b/fsharp-backend/src/LibService/HSTS.fs @@ -0,0 +1,9 @@ +module LibService.HSTS + +open System +open Microsoft.AspNetCore.HttpsPolicy + +let setConfig (options: HstsOptions) = + options.Preload <- true + options.IncludeSubDomains <- true + options.MaxAge <- TimeSpan.FromDays 365 diff --git a/fsharp-backend/src/LibService/LibService.fsproj b/fsharp-backend/src/LibService/LibService.fsproj index b9fae0f7d8..055af052d5 100644 --- a/fsharp-backend/src/LibService/LibService.fsproj +++ b/fsharp-backend/src/LibService/LibService.fsproj @@ -22,6 +22,7 @@ + diff --git a/fsharp-backend/tests/Tests/ApiServer.Tests.fs b/fsharp-backend/tests/Tests/ApiServer.Tests.fs index 40dd7506b5..a8ed4e0890 100644 --- a/fsharp-backend/tests/Tests/ApiServer.Tests.fs +++ b/fsharp-backend/tests/Tests/ApiServer.Tests.fs @@ -328,7 +328,7 @@ let postApiTestCase clear "Server-Timing" clear "x-darklang-execution-id" let (_ : bool) = h.Remove "Connection" // not useful, not in new API - let (_ : bool) = h.Remove "Strict-Transport-Security" // only in new API + let (_ : bool) = h.Remove "Strict-Transport-Security" // only in new API Q: any adjustment needed here? h |> Seq.toList From a123217eccc0571fdc6cadd1a30bf7a83194ed3c Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Fri, 15 Apr 2022 17:48:37 -0400 Subject: [PATCH 02/12] Format code --- fsharp-backend/src/ApiServer/ApiServer.fs | 2 +- fsharp-backend/src/BwdServer/Server.fs | 2 +- fsharp-backend/src/LibService/HSTS.fs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fsharp-backend/src/ApiServer/ApiServer.fs b/fsharp-backend/src/ApiServer/ApiServer.fs index 67ad02a893..d9b6a4b0bb 100644 --- a/fsharp-backend/src/ApiServer/ApiServer.fs +++ b/fsharp-backend/src/ApiServer/ApiServer.fs @@ -190,7 +190,7 @@ let configureServices (services : IServiceCollection) : unit = LibService.Telemetry.TraceDBQueries |> LibService.Kubernetes.configureServices [ LibBackend.Init.legacyServerCheck ] |> fun s -> s.AddServerTiming() - |> fun s -> s.AddHsts (LibService.HSTS.setConfig) + |> fun s -> s.AddHsts(LibService.HSTS.setConfig) |> ignore let run (packages : Packages) : unit = diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index d68f8d92f9..aa6c558120 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -488,7 +488,7 @@ let configureServices (services : IServiceCollection) : unit = |> Kubernetes.configureServices [ LibBackend.Init.legacyServerCheck ] |> Rollbar.AspNet.addRollbarToServices |> Telemetry.AspNet.addTelemetryToServices "BwdServer" Telemetry.TraceDBQueries - |> fun s -> s.AddHsts (LibService.HSTS.setConfig) + |> fun s -> s.AddHsts(LibService.HSTS.setConfig) |> ignore let noLogger (builder : ILoggingBuilder) : unit = diff --git a/fsharp-backend/src/LibService/HSTS.fs b/fsharp-backend/src/LibService/HSTS.fs index 574b5b98c3..5ea0054117 100644 --- a/fsharp-backend/src/LibService/HSTS.fs +++ b/fsharp-backend/src/LibService/HSTS.fs @@ -3,7 +3,7 @@ module LibService.HSTS open System open Microsoft.AspNetCore.HttpsPolicy -let setConfig (options: HstsOptions) = +let setConfig (options : HstsOptions) = options.Preload <- true options.IncludeSubDomains <- true options.MaxAge <- TimeSpan.FromDays 365 From e654257aaa1e9739ecf7edaf6c773cce82908206 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Fri, 15 Apr 2022 17:50:02 -0400 Subject: [PATCH 03/12] Add ApiServer HSTS test; tidy ApiServer tests --- fsharp-backend/tests/Tests/ApiServer.Tests.fs | 250 ++++++++++-------- 1 file changed, 140 insertions(+), 110 deletions(-) diff --git a/fsharp-backend/tests/Tests/ApiServer.Tests.fs b/fsharp-backend/tests/Tests/ApiServer.Tests.fs index a8ed4e0890..2aae57bfe2 100644 --- a/fsharp-backend/tests/Tests/ApiServer.Tests.fs +++ b/fsharp-backend/tests/Tests/ApiServer.Tests.fs @@ -44,7 +44,7 @@ let portFor (server : Server) : int = let forceLogin (username : UserName.T) : Task = task { let! user = LibBackend.Account.getUser username // validate user exists - let user = Exception.unwrapOptionInternal "" [] user + let _user = Exception.unwrapOptionInternal "" [] user let! authData = LibBackend.Session.insert username let cookie = System.Net.Cookie( @@ -98,7 +98,6 @@ let login (username : string) (password : string) : Task = return { http = client; csrf = csrfToken } } - let getAsync (server : Server) (client : C) @@ -155,26 +154,32 @@ let getInitialLoad (client : C) (canvasName : CanvasName.T) : Task = task { - let! (o : HttpResponseMessage) = + let! (ocamlResponse : HttpResponseMessage) = try getAsync OCaml client $"/a/{canvas}/" with | e -> Exception.raiseInternal "exception getting ocaml data" [ "canvas", canvas ] e - let! (f : HttpResponseMessage) = + let! (fsharpResponse : HttpResponseMessage) = try getAsync FSharp client $"/a/{canvas}/" with | e -> - Exception.raiseInternal "exception getting fs data" [ "canvas", canvas ] e + Exception.raiseInternal + "exception getting fsharp data" + [ "canvas", canvas ] + e - Expect.equal o.StatusCode f.StatusCode $"status code in {canvas}" + Expect.equal + ocamlResponse.StatusCode + fsharpResponse.StatusCode + $"status code in {canvas}" - let! oc = o.Content.ReadAsStringAsync() - let! fc = f.Content.ReadAsStringAsync() + let! ocamlContent = ocamlResponse.Content.ReadAsStringAsync() + let! fsharpContent = fsharpResponse.Content.ReadAsStringAsync() - let parse (s : string) : string * List = + let parseFns (s : string) : string * List = match s with | RegexAny "(.*const complete = )(\[.*\])(;\n.*)" [ before; fns; after ] -> let text = $"{before}{after}" @@ -187,87 +192,83 @@ let testUiReturnsTheSame (client : C) (canvas : CanvasName.T) : Task = (text, fns) | _ -> Exception.raiseInternal "doesn't match" [ "string", s ] - let oc, ocfns = parse oc - let fc, fcfns = parse fc + let ocamlContent, ocamlFns = parseFns ocamlContent + let actualFsharpContent, fsharpFns = parseFns fsharpContent let port = portFor OCaml - // There have been some tiny changes, let's work around them. The values here are the NEW values - let ocfns = - List.map - (fun (fn : Functions.FunctionMetadata) -> - match fn.name with - | "assoc" -> - { fn with - description = "Returns a copy of `dict` with the `key` set to `val`." - parameters = - [ { name = "dict" - tipe = "Dict" - block_args = [] - optional = false - description = "" } - { name = "key" - tipe = "Str" - block_args = [] - optional = false - description = "" } - { name = "val" - tipe = "Any" - block_args = [] - optional = false - description = "" } ] } - | "dissoc" -> - { fn with - parameters = - [ { name = "dict" - tipe = "Dict" - block_args = [] - optional = false - description = "" } - { name = "key" - tipe = "Str" - block_args = [] - optional = false - description = "" } ] - description = - "If the `dict` contains `key`, returns a copy of `dict` with `key` and its associated value removed. Otherwise, returns `dict` unchanged." } - | "Object::empty" -> - { fn with description = "Returns an empty dictionary." } - | "Object::merge" -> - { fn with - description = - "Returns a combined dictionary with both dictionaries' entries. If the same key exists in both `left` and `right`, it will have the value from `right`." } - | "Object::toJSON_v1" -> - { fn with - description = "Returns `dict` as a JSON string." - parameters = - [ { name = "dict" - tipe = "Dict" - block_args = [] - optional = false - description = "" } ] } - | "DB::queryOneWithKey_v2" -> - { fn with - description = - fn.description + ". Previously called DB::queryOnewithKey_v2" } - | "DB::queryWithKey_v2" -> - { fn with - description = - fn.description + ". Previous called DB::queryWithKey_v2" } - | "DB::query_v3" -> - { fn with - description = fn.description + ". Previously called DB::query_v3" } - | "DB::query_v2" -> - { fn with - description = fn.description + ". Previously called DB::query_v3" } - | "DB::queryOne_v2" -> - { fn with - description = fn.description + ". Previously called DB::queryOne_v2" } - | _ -> fn) - ocfns - - let oc = - oc + // There have been some tiny changes, let's work around them. + // The values here are the NEW values + let ocamlFns = + ocamlFns + |> List.map (fun (fn : Functions.FunctionMetadata) -> + match fn.name with + | "assoc" -> + { fn with + description = "Returns a copy of `dict` with the `key` set to `val`." + parameters = + [ { name = "dict" + tipe = "Dict" + block_args = [] + optional = false + description = "" } + { name = "key" + tipe = "Str" + block_args = [] + optional = false + description = "" } + { name = "val" + tipe = "Any" + block_args = [] + optional = false + description = "" } ] } + | "dissoc" -> + { fn with + parameters = + [ { name = "dict" + tipe = "Dict" + block_args = [] + optional = false + description = "" } + { name = "key" + tipe = "Str" + block_args = [] + optional = false + description = "" } ] + description = + "If the `dict` contains `key`, returns a copy of `dict` with `key` and its associated value removed. Otherwise, returns `dict` unchanged." } + | "Object::empty" -> { fn with description = "Returns an empty dictionary." } + | "Object::merge" -> + { fn with + description = + "Returns a combined dictionary with both dictionaries' entries. If the same key exists in both `left` and `right`, it will have the value from `right`." } + | "Object::toJSON_v1" -> + { fn with + description = "Returns `dict` as a JSON string." + parameters = + [ { name = "dict" + tipe = "Dict" + block_args = [] + optional = false + description = "" } ] } + | "DB::queryOneWithKey_v2" -> + { fn with + description = + fn.description + ". Previously called DB::queryOnewithKey_v2" } + | "DB::queryWithKey_v2" -> + { fn with + description = fn.description + ". Previous called DB::queryWithKey_v2" } + | "DB::query_v3" -> + { fn with description = fn.description + ". Previously called DB::query_v3" } + | "DB::query_v2" -> + { fn with description = fn.description + ". Previously called DB::query_v3" } + | "DB::queryOne_v2" -> + { fn with + description = fn.description + ". Previously called DB::queryOne_v2" } + | _ -> fn) + + let expectedFsharpContent = + ocamlContent // a couple of specific ones .Replace( $"static.darklang.localhost:{port}", @@ -283,12 +284,12 @@ let testUiReturnsTheSame (client : C) (canvas : CanvasName.T) : Task = $"localhost:{LibService.Config.apiServerNginxPort}" ) - Expect.equal fc oc "" + Expect.equal actualFsharpContent expectedFsharpContent "" List.iter2 (fun (ffn : Functions.FunctionMetadata) ofn -> Expect.equal ffn ofn ffn.name) - fcfns - ocfns + fsharpFns + ocamlFns } type ApiResponse<'a> = Task<'a * System.Net.HttpStatusCode * Map> @@ -314,21 +315,22 @@ let postApiTestCase with | e -> print - $"Error deserializing {server} in {canvasName}/{api} with body\n\n{requestBody}\n\n and response\n\n{responseBody}" + $"Error deserializing or canonicalizing {server} in {canvasName}/{api} with body\n\n{requestBody}\n\n and response\n\n{responseBody}" e.Reraise() let headerMap (h : Headers.HttpResponseHeaders) : Map = let clear str = if h.Contains str then - (h.Remove str |> ignore - h.TryAddWithoutValidation(str, "XXX") |> ignore) + h.Remove str |> ignore + h.TryAddWithoutValidation(str, "XXX") |> ignore clear "Date" clear "Server" clear "Server-Timing" clear "x-darklang-execution-id" - let (_ : bool) = h.Remove "Connection" // not useful, not in new API - let (_ : bool) = h.Remove "Strict-Transport-Security" // only in new API Q: any adjustment needed here? + + h.Remove "Connection" |> ignore // not useful, not in new API + h.Remove "Strict-Transport-Security" |> ignore // only in new API h |> Seq.toList @@ -337,7 +339,6 @@ let postApiTestCase let headers = headerMap response.Headers return (result, response.StatusCode, headers) - } let postApiTest @@ -355,12 +356,11 @@ let postApiTest let! (fContent, fStatus, fHeaders) as f = postApiTestCase client canvasName FSharp api body deserialize canonicalizeBody - let () = - if oStatus <> fStatus then - print ( - $"Non-matching status codes: {api}\n\nbody:\n{body}\n\n" - + $"ocaml:\n{o}\n\nfsharp:\n{f}" - ) + if oStatus <> fStatus then + print ( + $"Non-matching status codes: {api}\n\nbody:\n{body}\n\n" + + $"ocaml:\n{o}\n\nfsharp:\n{f}" + ) Expect.equal fStatus oStatus "status" Expect.equal fContent oContent "content" @@ -368,7 +368,7 @@ let postApiTest } -let testGetTraceData (client : C) (canvasName : CanvasName.T) = +let testGetTraceData (client : C) (canvasName : CanvasName.T) : Task = task { let! (o : HttpResponseMessage) = postAsync OCaml client $"/api/{canvasName}/all_traces" "" @@ -387,14 +387,15 @@ let testGetTraceData (client : C) (canvasName : CanvasName.T) = timestamp = td.timestamp.truncate () input = td.input |> List.sortBy (fun (k, v) -> k) }) }) - return! + do! body |> deserialize |> fun ts -> ts.traces |> Task.iterInParallel (fun (tlid, traceID) -> task { let (ps : Traces.TraceData.Params) = { tlid = tlid; trace_id = traceID } - return! + + do! postApiTest "get_trace_data" (serialize ps) @@ -416,7 +417,7 @@ let testDBStats (client : C) (canvasName : CanvasName.T) : Task = |> List.map (fun db -> db.tlid) |> fun tlids -> ({ tlids = tlids } : DBs.DBStats.Params) - return! + do! postApiTest "get_db_stats" (serialize parameters) @@ -471,7 +472,7 @@ let testTriggerHandler (client : C) (canvasName : CanvasName.T) = input = [ "user", ORT.DStr "test" ] trace_id = System.Guid.NewGuid() } - return! + do! postApiTest "trigger_handler" (serialize body) @@ -488,7 +489,7 @@ let testWorkerStats (client : C) (canvasName : CanvasName.T) : Task = let! canvas = Canvas.getMeta canvasName let! canvasWithJustWorkers = Canvas.loadAllWorkers canvas - return! + do! canvasWithJustWorkers.handlers |> Map.values |> List.filterMap (fun h -> @@ -672,6 +673,35 @@ let canonicalizeAst (e : OT.RuntimeT.fluidExpr) = | other -> other) e + +let testHsts (client : C) (canvasName : CanvasName.T) = + task { + let server = FSharp + let requestBody = "" + // TODO: should we check a few, instead of just initial_load? + // How many would be reasonable? Any particular ones come to mind? + // If we test a few, we'll have to pair the `api` variable + // with appropriate `requestBody`s. + let api = "initial_load" + + let! (response : HttpResponseMessage) = + postAsync server client $"/api/{canvasName}/{api}" requestBody + + let hstsHeader = + response.Headers + |> Seq.toList + |> List.choose (fun (KeyValue (x, y)) -> + if x.ToLower() = "strict-transport-security" then + Some(String.concat "," y) + else + None) + + Expect.equal + [ "max-age=31536000; includeSubDomains; preload" ] + hstsHeader + "strict-transport-security header not present" + } + let testInitialLoadReturnsTheSame (client : C) (canvasName : CanvasName.T) = let deserialize v = Json.OCamlCompatible.deserialize v @@ -798,10 +828,11 @@ let localOnlyTests = "packages", testPackages "trigger handler", testTriggerHandler "delete 404s", testDelete404s + "hsts", testHsts // TODO upload_package // worker_schedule tested by hand ] - |> List.map (fun (name, fn) -> testTask name { return! fn c cn }) + |> List.map (fun (name, fn) -> testTask name { do! fn c cn }) else [] @@ -934,5 +965,4 @@ let cookies = (302, None, Some "/login?error=Invalid+username+or+password")) (local, None, (302, None, Some "/login?error=Invalid+username+or+password")) ] - let tests = testList "ApiServer" [ localOnlyTests; permissions; cookies ] From b46391f150d3ba3a9ecc416a7cb2e4cba20d2d98 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Fri, 15 Apr 2022 18:34:14 -0400 Subject: [PATCH 04/12] Provide commentary in BwdServer.Tests.fs --- fsharp-backend/tests/Tests/BwdServer.Tests.fs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fsharp-backend/tests/Tests/BwdServer.Tests.fs b/fsharp-backend/tests/Tests/BwdServer.Tests.fs index 6cc43fd8e4..df5d3a5d8f 100644 --- a/fsharp-backend/tests/Tests/BwdServer.Tests.fs +++ b/fsharp-backend/tests/Tests/BwdServer.Tests.fs @@ -1,3 +1,8 @@ +/// Tests the built-with-Dark server (`BwdServer`), +/// which is the server that runs Dark users' HTTP handlers. +/// +/// Test files are stored in the `tests/httptestfiles` directory, +/// which includes a README.md of how these tests work. module Tests.BwdServer open Expecto From f785fcfa61be345f61f1435025e608b7c5c4d6af Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Mon, 18 Apr 2022 17:57:51 -0400 Subject: [PATCH 05/12] Adjust BwdServer HSTS set-up, including tests The implementation here is a bit of a hack; for some reason, the constructs used in ApiServer.fs are not working here. A follow-up commit is likely to clean this up. Note: 205/211 needed adjustment; the other 6 did not, as the requests do not hit our HTTP stack Several (5) of the tests required careful adjustment through vim because of their inconsistent line endings. --- fsharp-backend/src/BwdServer/Server.fs | 7 +++---- fsharp-backend/tests/Tests/BwdServer.Tests.fs | 2 +- .../tests/httptestfiles/404-head.test | 1 + .../tests/httptestfiles/404-no-canvas.test | 1 + .../tests/httptestfiles/404-no-user.test | 1 + fsharp-backend/tests/httptestfiles/404.test | 1 + .../tests/httptestfiles/500-clash.test | 1 + fsharp-backend/tests/httptestfiles/README.md | 7 +++++++ ...o-setting-header-localhost-3000-https.test | 1 + ...cors-no-setting-header-localhost-3000.test | 1 + ...cors-no-setting-header-localhost-5000.test | 1 + ...cors-no-setting-header-localhost-8000.test | 1 + ...cors-no-setting-header-localhost-8001.test | 1 + .../cors-no-setting-header-none.test | 1 + .../cors-no-setting-header-null.test | 1 + ...setting-options-header-localhost-3000.test | 1 + ...setting-options-header-localhost-3001.test | 1 + .../cors-no-setting-options-header-none.test | 1 + .../cors-no-setting-options-header-null.test | 1 + .../cors-no-setting-options-header-url.test | 1 + .../cors-one-header-different-casing.test | 1 + .../cors-one-header-different-domain.test | 1 + .../cors-one-header-different-path.test | 1 + .../cors-one-header-different-port.test | 1 + .../cors-one-header-different-scheme.test | 1 + .../cors-one-header-matching.test | 1 + .../httptestfiles/cors-one-header-none.test | 1 + .../httptestfiles/cors-one-header-null.test | 1 + ...s-one-options-header-different-casing.test | 1 + ...s-one-options-header-different-domain.test | 1 + ...ors-one-options-header-different-path.test | 1 + ...ors-one-options-header-different-port.test | 1 + ...s-one-options-header-different-scheme.test | 1 + .../cors-one-options-header-matching.test | 1 + .../cors-one-options-header-none.test | 1 + .../cors-one-options-header-null.test | 1 + .../httptestfiles/cors-star-header-none.test | 1 + .../httptestfiles/cors-star-header-null.test | 1 + .../httptestfiles/cors-star-header-value.test | 1 + .../httptestfiles/cors-star-options.test | 1 + .../cors-two-header-different-casing.test | 1 + .../cors-two-header-different-domain.test | 1 + .../cors-two-header-different-path.test | 1 + .../cors-two-header-different-port.test | 1 + .../cors-two-header-different-scheme.test | 1 + .../cors-two-header-matching.test | 1 + .../httptestfiles/cors-two-header-none.test | 1 + .../httptestfiles/cors-two-header-null.test | 1 + .../tests/httptestfiles/favicon-fallback.test | Bin 1198 -> 1284 bytes .../tests/httptestfiles/favicon-handler.test | 1 + .../tests/httptestfiles/method-custom.test | 1 + .../tests/httptestfiles/method-delete.test | 1 + .../tests/httptestfiles/method-get.test | 1 + .../httptestfiles/method-head-to-get.test | 1 + .../tests/httptestfiles/method-head.test | 1 + .../tests/httptestfiles/method-options.test | 1 + .../tests/httptestfiles/method-patch.test | 1 + .../tests/httptestfiles/method-post.test | 1 + .../tests/httptestfiles/method-put.test | 1 + .../httptestfiles/post-no-content-length.test | 1 + .../request-cookie-double-with-semicolon.test | 1 + .../httptestfiles/request-cookie-double.test | 1 + .../httptestfiles/request-cookie-empty.test | 1 + .../request-cookie-missing-header.test | 1 + .../request-cookie-name-with-equals.test | 1 + .../request-cookie-name-without-equals.test | 1 + .../request-cookie-single-with-semicolon.test | 1 + .../httptestfiles/request-cookie-single.test | 1 + .../request-cookie-url-encoded.test | 1 + .../tests/httptestfiles/request-empty.test | 1 + .../httptestfiles/request-form-array.test | 1 + .../httptestfiles/request-form-basic.test | 1 + .../httptestfiles/request-form-chars.test | 1 + .../httptestfiles/request-form-chars2.test | 1 + .../httptestfiles/request-form-comma.test | 1 + .../tests/httptestfiles/request-form-dup.test | 1 + .../request-form-empty-param-equals.test | 1 + .../request-form-empty-param.test | 1 + .../httptestfiles/request-form-encoding.test | 1 + .../httptestfiles/request-form-multipart.test | 1 + .../request-form-unicode-invalid.test | 1 + .../request-form-unicode-valid.test | 1 + .../httptestfiles/request-get-empty-body.test | 1 + .../request-header-accept-encoding-br.test | 1 + ...quest-header-accept-encoding-compress.test | 1 + ...equest-header-accept-encoding-deflate.test | 1 + .../request-header-accept-encoding-gzip.test | 1 + ...quest-header-accept-encoding-identity.test | 1 + .../request-header-accept-encoding-many.test | 1 + .../request-header-accept-encoding-star.test | 1 + .../request-header-accept-image.test | 1 + .../request-header-accept-language-1.test | 1 + .../request-header-accept-many.test | 1 + .../request-header-accept-star.test | 1 + .../request-header-accept-text.test | 1 + ...equest-header-cache-control-gibberish.test | 1 + .../request-header-cache-control-max-age.test | 1 + ...request-header-cache-control-no-cache.test | 1 + .../request-header-cache-control-nostore.test | 1 + ...uest-header-cache-control-notransform.test | 1 + ...est-header-cache-control-onlyifcached.test | 1 + .../request-header-case-insensitive.test | 1 + .../request-header-content-encoding-br.test | 1 + .../request-header-content-type-ping.test | 1 + ...request-header-missing-content-length.test | 1 + .../request-header-number-string.test | 1 + .../request-header-space-insensitive.test | 1 + .../request-header-todo-accept.test | 1 + ...est-header-transfer-encoding-bad-size.test | 15 ++++++++------- ...t-header-transfer-encoding-incomplete.test | 1 + ...request-header-transfer-encoding-json.test | 1 + ...request-header-transfer-encoding-slow.test | 1 + ...equest-header-transfer-encoding-valid.test | 1 + .../request-header-unicode-invalid-value.test | 1 + .../request-header-unicode-valid.test | 1 + .../request-header-user-agent.test | 1 + ...equest-header-x-forwarded-for-invalid.test | 1 + .../request-header-x-forwarded-for-valid.test | 1 + ...quest-header-x-forwarded-host-invalid.test | 1 + ...request-header-x-forwarded-host-valid.test | 1 + .../request-json-caps-charset.test | 1 + .../httptestfiles/request-json-comments.test | 1 + .../request-json-invalid-chars.test | 1 + .../request-json-invalid-string.test | 1 + .../request-json-no-charset.test | 1 + .../httptestfiles/request-json-value-int.test | 1 + .../request-json-value-list.test | 1 + ...request-json-value-obj-trailing-comma.test | 1 + .../httptestfiles/request-json-value-obj.test | 1 + .../request-json-value-string.test | 1 + .../request-path-end-0-to-3.test | 1 + .../request-path-end-1-to-3.test | 1 + .../request-path-end-2-to-3.test | 1 + .../request-path-end-3-to-0.test | 1 + .../request-path-end-3-to-1.test | 1 + .../request-path-end-3-to-2.test | 1 + .../request-path-end-3-to-3.test | 1 + .../request-path-middle-1-to-3.test | 1 + .../request-path-middle-2-to-3.test | 1 + .../request-path-middle-3-to-1.test | 1 + .../request-path-middle-3-to-2.test | 1 + .../request-path-middle-3-to-3.test | 1 + .../request-path-start-1-to-3.test | 1 + .../request-path-start-2-to-3.test | 1 + .../request-path-start-3-to-0.test | 1 + .../request-path-start-3-to-1.test | 1 + .../request-path-start-3-to-2.test | 1 + .../request-path-start-3-to-3.test | 1 + .../request-queryparams-chars.test | 1 + .../request-queryparams-chars2.test | 1 + .../request-queryparams-comma.test | 1 + .../request-queryparams-dup.test | 1 + ...equest-queryparams-empty-param-equals.test | 1 + .../request-queryparams-empty-param.test | 1 + .../tests/httptestfiles/request-text.test | 1 + .../tests/httptestfiles/request-url.test | 1 + .../httptestfiles/response-basic-bytes.test | 1 + .../httptestfiles/response-basic-date.test | 1 + .../response-basic-derror-in-obj.test | 1 + .../httptestfiles/response-basic-derror.test | 1 + .../response-basic-http-success-int.test | 1 + .../response-basic-http-success-obj.test | 1 + .../response-basic-incomplete.test | 1 + .../httptestfiles/response-basic-int.test | 1 + .../response-basic-list-empty.test | 1 + .../httptestfiles/response-basic-list.test | 1 + .../response-basic-obj-empty.test | 1 + .../httptestfiles/response-basic-obj.test | 1 + .../response-basic-option-just.test | 1 + .../response-basic-option-nothing.test | 1 + .../response-basic-result-error.test | 1 + .../response-basic-result-ok.test | 1 + .../httptestfiles/response-basic-string.test | 1 + .../httptestfiles/response-basic-uuid.test | 1 + .../response-errorrail-option-just.test | 1 + .../response-errorrail-option-nothing.test | 1 + .../response-errorrail-result-error.test | 1 + .../response-errorrail-result-ok.test | 1 + .../response-header-custom-server.test | 1 + .../response-incorrect-length.test | 1 + .../httptestfiles/response-int-html.test | 1 + .../httptestfiles/response-int-json.test | 1 + .../httptestfiles/response-int-no-ct.test | 1 + .../httptestfiles/response-int-text.test | 1 + .../httptestfiles/response-list-html.test | 1 + .../httptestfiles/response-list-json.test | 1 + .../httptestfiles/response-list-no-ct.test | 1 + .../httptestfiles/response-list-text.test | 1 + .../httptestfiles/response-obj-html.test | 1 + .../httptestfiles/response-obj-json.test | 1 + .../httptestfiles/response-obj-no-ct.test | 1 + .../httptestfiles/response-obj-text.test | 1 + .../httptestfiles/response-redirect.test | 1 + .../httptestfiles/response-string-html.test | 1 + .../httptestfiles/response-string-json.test | 1 + .../httptestfiles/response-string-no-ct.test | 1 + .../httptestfiles/response-string-text.test | 1 + .../tests/httptestfiles/secrets.test | 1 + .../tests/httptestfiles/sitemap-fallback.test | 1 + .../tests/httptestfiles/sitemap-handler.test | 1 + .../httptestfiles/url-custom-domain.test | 1 + .../httptestfiles/url-custom-scheme.test | 1 + .../tests/httptestfiles/url-http.test | 1 + .../tests/httptestfiles/url-https.test | 1 + .../tests/httptestfiles/vars-missing.test | 1 + .../httptestfiles/vars-multisegment.test | 1 + .../vars-specificity-implemented.test | 1 + fsharp-backend/tests/httptestfiles/vars.test | 1 + 208 files changed, 222 insertions(+), 12 deletions(-) diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index aa6c558120..8bbcf75133 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -314,8 +314,8 @@ let runDarkHandler // and leave it to middleware to say what it wants to do with that let searchMethod = if method = "HEAD" then "GET" else method - /// Canvas to process request against, - /// with enough loaded to handle this request + // Canvas to process request against, + // with enough loaded to handle this request let! canvas = Canvas.loadHttpHandlers meta requestPath searchMethod let url : string = ctx.Request.GetEncodedUrl() |> canonicalizeURL (isHttps ctx) @@ -423,6 +423,7 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = (task { let executionID = LibService.Telemetry.executionID () ctx.Items[ "executionID" ] <- executionID + setHeader ctx "Strict-Transport-Security" "max-age=31536000; includeSubDomains; preload" setHeader ctx "x-darklang-execution-id" (string executionID) setHeader ctx "Server" "darklang" @@ -480,7 +481,6 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = |> fun app -> app.UseRouting() // must go after UseRouting |> Kubernetes.configureApp healthCheckPort - |> fun app -> app.UseHsts() |> fun app -> app.Run(RequestDelegate handler) let configureServices (services : IServiceCollection) : unit = @@ -488,7 +488,6 @@ let configureServices (services : IServiceCollection) : unit = |> Kubernetes.configureServices [ LibBackend.Init.legacyServerCheck ] |> Rollbar.AspNet.addRollbarToServices |> Telemetry.AspNet.addTelemetryToServices "BwdServer" Telemetry.TraceDBQueries - |> fun s -> s.AddHsts(LibService.HSTS.setConfig) |> ignore let noLogger (builder : ILoggingBuilder) : unit = diff --git a/fsharp-backend/tests/Tests/BwdServer.Tests.fs b/fsharp-backend/tests/Tests/BwdServer.Tests.fs index df5d3a5d8f..4a4021b5ce 100644 --- a/fsharp-backend/tests/Tests/BwdServer.Tests.fs +++ b/fsharp-backend/tests/Tests/BwdServer.Tests.fs @@ -264,7 +264,7 @@ let t filename = match k, v with | "Date", _ -> k, "xxx, xx xxx xxxx xx:xx:xx xxx" | "x-darklang-execution-id", _ -> k, "0123456789" - | other -> (k, v)) + | _other -> (k, v)) |> List.sortBy Tuple2.first // CLEANUP ocaml headers are sorted, inexplicably let normalizeExpectedHeaders diff --git a/fsharp-backend/tests/httptestfiles/404-head.test b/fsharp-backend/tests/httptestfiles/404-head.test index 60ae395ee7..2f4e757cdd 100644 --- a/fsharp-backend/tests/httptestfiles/404-head.test +++ b/fsharp-backend/tests/httptestfiles/404-head.test @@ -19,4 +19,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 31 diff --git a/fsharp-backend/tests/httptestfiles/404-no-canvas.test b/fsharp-backend/tests/httptestfiles/404-no-canvas.test index 1a7a3710f0..f796dfcce7 100644 --- a/fsharp-backend/tests/httptestfiles/404-no-canvas.test +++ b/fsharp-backend/tests/httptestfiles/404-no-canvas.test @@ -19,6 +19,7 @@ access-control-allow-origin: * // OCAMLONLY Access-Control-Allow-Origin: * // FSHARPONLY Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY x-darklang-execution-id: 0123456789 404 Not Found: No route matches \ No newline at end of file diff --git a/fsharp-backend/tests/httptestfiles/404-no-user.test b/fsharp-backend/tests/httptestfiles/404-no-user.test index 92c460b88e..c2fa62f780 100644 --- a/fsharp-backend/tests/httptestfiles/404-no-user.test +++ b/fsharp-backend/tests/httptestfiles/404-no-user.test @@ -17,6 +17,7 @@ Content-Length: 14 Content-Type: text/plain; charset=utf-8 // FSHARPONLY Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY x-darklang-execution-id: 0123456789 user not found \ No newline at end of file diff --git a/fsharp-backend/tests/httptestfiles/404.test b/fsharp-backend/tests/httptestfiles/404.test index b5397e7d2c..4747db140a 100644 --- a/fsharp-backend/tests/httptestfiles/404.test +++ b/fsharp-backend/tests/httptestfiles/404.test @@ -19,6 +19,7 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 31 404 Not Found: No route matches \ No newline at end of file diff --git a/fsharp-backend/tests/httptestfiles/500-clash.test b/fsharp-backend/tests/httptestfiles/500-clash.test index d75b363489..b93a503886 100644 --- a/fsharp-backend/tests/httptestfiles/500-clash.test +++ b/fsharp-backend/tests/httptestfiles/500-clash.test @@ -22,6 +22,7 @@ Connection: keep-alive // OCAMLONLY Content-Length: 61 Access-Control-Allow-Origin: * // FSHARPONLY access-control-allow-origin: * // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY x-darklang-execution-id: 0123456789 500 Internal Server Error: More than one handler for route: / \ No newline at end of file diff --git a/fsharp-backend/tests/httptestfiles/README.md b/fsharp-backend/tests/httptestfiles/README.md index 2b5ed92b34..98ebbff316 100644 --- a/fsharp-backend/tests/httptestfiles/README.md +++ b/fsharp-backend/tests/httptestfiles/README.md @@ -101,6 +101,13 @@ editors can change the contents, for example stripping \r characters, or adding removing newlines. To get around this, you can use a hex editor, or `vim -b` with `noeol` set. You should also add the files to .gitattributes in this directory. +In case you're not a `vim` expert, here are some steps to follow: +- `vim ./file-path.test -b` +- hit `i` to enter insert mode +- type/paste your changes, being careful to not edit anything you don't mean to +- hit `esc` to escape edit mode +- type `:wq` and press `enter` to escape + # Adjusting for minor differences ## Json responses diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000-https.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000-https.test index 7bac3a9bb2..3b3b0bade7 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000-https.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000-https.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000.test index c2f252d329..9ee919db27 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-3000.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-5000.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-5000.test index fed07e9500..3200488f29 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-5000.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-5000.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8000.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8000.test index 35054bb6f0..ffc7472ff4 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8000.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8000.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8001.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8001.test index 399fbfe7aa..6bf516213f 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8001.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-localhost-8001.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-none.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-none.test index 82d2b1c3fa..8ca15f89ba 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-none.test @@ -19,6 +19,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-null.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-null.test index 777539c9a3..6b869c63bf 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-header-null.test @@ -20,6 +20,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3000.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3000.test index 4ab93f2cf2..a612f09cfd 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3000.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3000.test @@ -23,4 +23,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3001.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3001.test index 89df1ea483..adbbf5fcb5 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3001.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-localhost-3001.test @@ -23,4 +23,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-none.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-none.test index 6179393128..1cfeb76362 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-none.test @@ -22,4 +22,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-null.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-null.test index e606ee2564..48ea6861ea 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-null.test @@ -24,4 +24,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-url.test b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-url.test index 24d53e5dc7..fffce85ae2 100644 --- a/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-url.test +++ b/fsharp-backend/tests/httptestfiles/cors-no-setting-options-header-url.test @@ -23,4 +23,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-different-casing.test b/fsharp-backend/tests/httptestfiles/cors-one-header-different-casing.test index 7cc08cfdf0..477f65308b 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-different-casing.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-different-casing.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-different-domain.test b/fsharp-backend/tests/httptestfiles/cors-one-header-different-domain.test index caac6e51b5..aa5a48a53d 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-different-domain.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-different-domain.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-different-path.test b/fsharp-backend/tests/httptestfiles/cors-one-header-different-path.test index ee45381e76..bbe6ba5b2b 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-different-path.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-different-path.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-different-port.test b/fsharp-backend/tests/httptestfiles/cors-one-header-different-port.test index e9f2795aa6..c585f4a8fe 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-different-port.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-different-port.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-different-scheme.test b/fsharp-backend/tests/httptestfiles/cors-one-header-different-scheme.test index 458db22411..3d1da3a207 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-different-scheme.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-different-scheme.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-matching.test b/fsharp-backend/tests/httptestfiles/cors-one-header-matching.test index fc513e0387..7d8bb96760 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-matching.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-matching.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-none.test b/fsharp-backend/tests/httptestfiles/cors-one-header-none.test index cc9452261a..0d32d01c68 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-none.test @@ -19,6 +19,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-header-null.test b/fsharp-backend/tests/httptestfiles/cors-one-header-null.test index 004c2bd960..5004daf7a3 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-header-null.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-casing.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-casing.test index f3144ad497..e6a71ccae2 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-casing.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-casing.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-domain.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-domain.test index 42f45b8f1c..a9e49a9a4f 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-domain.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-domain.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-path.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-path.test index 17477c29ee..6b82d3c0e4 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-path.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-path.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-port.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-port.test index c6db72eb68..1eae74c842 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-port.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-port.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-scheme.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-scheme.test index 209039a8cb..e0b686b903 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-scheme.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-different-scheme.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-matching.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-matching.test index c0a63bb50d..e3b475b0b5 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-matching.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-matching.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-none.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-none.test index 2995eda898..361127721a 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-none.test @@ -18,4 +18,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-one-options-header-null.test b/fsharp-backend/tests/httptestfiles/cors-one-options-header-null.test index 24b05bb2bb..4b38697b84 100644 --- a/fsharp-backend/tests/httptestfiles/cors-one-options-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-one-options-header-null.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-star-header-none.test b/fsharp-backend/tests/httptestfiles/cors-star-header-none.test index 606d03dd54..e5dcc6091a 100644 --- a/fsharp-backend/tests/httptestfiles/cors-star-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-star-header-none.test @@ -21,6 +21,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-star-header-null.test b/fsharp-backend/tests/httptestfiles/cors-star-header-null.test index 2116f068b1..86e7f1eb5c 100644 --- a/fsharp-backend/tests/httptestfiles/cors-star-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-star-header-null.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-star-header-value.test b/fsharp-backend/tests/httptestfiles/cors-star-header-value.test index 454f0cf6c9..16bcae065e 100644 --- a/fsharp-backend/tests/httptestfiles/cors-star-header-value.test +++ b/fsharp-backend/tests/httptestfiles/cors-star-header-value.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-star-options.test b/fsharp-backend/tests/httptestfiles/cors-star-options.test index 755e099041..74400b5b8d 100644 --- a/fsharp-backend/tests/httptestfiles/cors-star-options.test +++ b/fsharp-backend/tests/httptestfiles/cors-star-options.test @@ -25,4 +25,5 @@ x-darklang-execution-id: 0123456789 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 0 diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-different-casing.test b/fsharp-backend/tests/httptestfiles/cors-two-header-different-casing.test index 0626dbade5..bfb6783c82 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-different-casing.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-different-casing.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-different-domain.test b/fsharp-backend/tests/httptestfiles/cors-two-header-different-domain.test index 24e6138edb..15b94739a0 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-different-domain.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-different-domain.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-different-path.test b/fsharp-backend/tests/httptestfiles/cors-two-header-different-path.test index aef90a91f6..b2afbe518f 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-different-path.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-different-path.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-different-port.test b/fsharp-backend/tests/httptestfiles/cors-two-header-different-port.test index f177c23e86..9dc4d3a8e5 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-different-port.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-different-port.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-different-scheme.test b/fsharp-backend/tests/httptestfiles/cors-two-header-different-scheme.test index 73af283dff..48bea18a0d 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-different-scheme.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-different-scheme.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-matching.test b/fsharp-backend/tests/httptestfiles/cors-two-header-matching.test index df44a9346d..8eeb671e0a 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-matching.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-matching.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-none.test b/fsharp-backend/tests/httptestfiles/cors-two-header-none.test index b293a9441e..d98a8d1dc5 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-none.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-none.test @@ -19,6 +19,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/cors-two-header-null.test b/fsharp-backend/tests/httptestfiles/cors-two-header-null.test index 9261dc7406..d4b5309a3a 100644 --- a/fsharp-backend/tests/httptestfiles/cors-two-header-null.test +++ b/fsharp-backend/tests/httptestfiles/cors-two-header-null.test @@ -22,6 +22,7 @@ Content-Type: text/plain; charset=utf-8 Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY Connection: keep-alive // OCAMLONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: 17 "hello alice-bob" diff --git a/fsharp-backend/tests/httptestfiles/favicon-fallback.test b/fsharp-backend/tests/httptestfiles/favicon-fallback.test index 7ec3a9c54c6b34809bf571761b4f22b25b02d101..2f0700925c0f5df9ae29c991533ec8832ac783ac 100644 GIT binary patch delta 94 zcmZ3-*}}Eq4P#(%Nl|8UiEcGVcs;#l1sj-=X xfq}I`W?phmX-aBvX_8BRZenI$v9&@$QEE Date: Mon, 18 Apr 2022 18:16:18 -0400 Subject: [PATCH 06/12] Format code --- fsharp-backend/src/BwdServer/Server.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index 4d1f83088c..45916bd990 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -423,7 +423,10 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = (task { let executionID = LibService.Telemetry.executionID () ctx.Items[ "executionID" ] <- executionID - setHeader ctx "Strict-Transport-Security" "max-age=31536000; includeSubDomains; preload" + setHeader + ctx + "Strict-Transport-Security" + "max-age=31536000; includeSubDomains; preload" setHeader ctx "x-darklang-execution-id" (string executionID) setHeader ctx "Server" "darklang" From 38725cbadd5cf044ad869c10afa14a033eca6ec9 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Mon, 18 Apr 2022 18:22:30 -0400 Subject: [PATCH 07/12] Update code comment --- fsharp-backend/tests/Tests/BwdServer.Tests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsharp-backend/tests/Tests/BwdServer.Tests.fs b/fsharp-backend/tests/Tests/BwdServer.Tests.fs index 30a5165866..405c30a20a 100644 --- a/fsharp-backend/tests/Tests/BwdServer.Tests.fs +++ b/fsharp-backend/tests/Tests/BwdServer.Tests.fs @@ -1,4 +1,4 @@ -/// Tests the built-with-Dark server (`BwdServer`), +/// Tests the builtwithdark.com server (`BwdServer`), /// which is the server that runs Dark users' HTTP handlers. /// /// Test files are stored in the `tests/httptestfiles` directory, From a4873e2e9412f8ecf0907054aac1c00c3fb9c777 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Tue, 19 Apr 2022 12:15:38 -0400 Subject: [PATCH 08/12] Fix bwdserver test (remove FSHARPONLY) --- .../httptestfiles/request-json-value-obj-trailing-comma.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fsharp-backend/tests/httptestfiles/request-json-value-obj-trailing-comma.test b/fsharp-backend/tests/httptestfiles/request-json-value-obj-trailing-comma.test index 777c2f0a09..70d752d7b5 100644 --- a/fsharp-backend/tests/httptestfiles/request-json-value-obj-trailing-comma.test +++ b/fsharp-backend/tests/httptestfiles/request-json-value-obj-trailing-comma.test @@ -17,7 +17,7 @@ Date: xxx, xx xxx xxxx xx:xx:xx xxx // FSHARPONLY Connection: keep-alive // OCAMLONLY Server: nginx/1.16.1 // OCAMLONLY Server: darklang // FSHARPONLY -x-darklang-execution-id: 0123456789 // FSHARPONLY +x-darklang-execution-id: 0123456789 Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Content-Length: LENGTH Content-Type: text/plain; charset=utf-8 // FSHARPONLY From 9c806eae9646a770f82353ca3221091469614d03 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Tue, 19 Apr 2022 12:52:04 -0400 Subject: [PATCH 09/12] Add HSTS header to serve-latest.test --- fsharp-backend/tests/httptestfiles/serve-latest.test | 1 + 1 file changed, 1 insertion(+) diff --git a/fsharp-backend/tests/httptestfiles/serve-latest.test b/fsharp-backend/tests/httptestfiles/serve-latest.test index 8fdade9cd5..c16a4dd1b8 100644 --- a/fsharp-backend/tests/httptestfiles/serve-latest.test +++ b/fsharp-backend/tests/httptestfiles/serve-latest.test @@ -46,5 +46,6 @@ x-goog-stored-content-encoding: identity x-goog-stored-content-length: 38 x-guploader-uploadid: xxxx // OCAMLONLY X-GUploader-UploadID: xxxx // FSHARPONLY +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload // FSHARPONLY Hello world From 151faf0cf3f7bfda954268f1812ded82aef30e64 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Tue, 19 Apr 2022 13:40:38 -0400 Subject: [PATCH 10/12] Expand HSTS testing of ApiServer --- fsharp-backend/tests/Tests/ApiServer.Tests.fs | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/fsharp-backend/tests/Tests/ApiServer.Tests.fs b/fsharp-backend/tests/Tests/ApiServer.Tests.fs index c0faf682d8..9ff05b223c 100644 --- a/fsharp-backend/tests/Tests/ApiServer.Tests.fs +++ b/fsharp-backend/tests/Tests/ApiServer.Tests.fs @@ -680,18 +680,7 @@ let canonicalizeAst (e : OT.RuntimeT.fluidExpr) = let testHsts (client : C) (canvasName : CanvasName.T) = - task { - let server = FSharp - let requestBody = "" - // TODO: should we check a few, instead of just initial_load? - // How many would be reasonable? Any particular ones come to mind? - // If we test a few, we'll have to pair the `api` variable - // with appropriate `requestBody`s. - let api = "initial_load" - - let! (response : HttpResponseMessage) = - postAsync server client $"/api/{canvasName}/{api}" requestBody - + let testHstsHeader (response : HttpResponseMessage) = let hstsHeader = response.Headers |> Seq.toList @@ -704,9 +693,35 @@ let testHsts (client : C) (canvasName : CanvasName.T) = Expect.equal [ "max-age=31536000; includeSubDomains; preload" ] hstsHeader - "strict-transport-security header not present" + "Strict-Transport-Security header either missing or incorrect" + + task { + let server = FSharp + + let! responses = + [ getAsync server client $"/api/{canvasName}/login" + getAsync server client $"/api/{canvasName}/logout" + getAsync server client $"/api/check-apiserver" + + getAsync server client $"/api/{canvasName}" + postAsync server client $"/api/{canvasName}/initial_load" "" + postAsync server client $"/api/{canvasName}/packages" "" + postAsync server client $"/api/{canvasName}/get_worker_stats" "" + postAsync server client $"/api/{canvasName}/get_404s" "" + postAsync server client $"/api/{canvasName}/all_traces" "" + postAsync server client $"/api/{canvasName}/get_db_stats" "" + postAsync server client $"/api/{canvasName}/get_trace_data" "" + postAsync server client $"/api/{canvasName}/get_unlocked_dbs" "" + + getAsync server client $"/completely-fake-url" + postAsync server client $"/completely-fake-url" "" ] + |> Task.WhenAll + + responses |> Array.iter testHstsHeader } + + let testInitialLoadReturnsTheSame (client : C) (canvasName : CanvasName.T) = let deserialize v = Json.OCamlCompatible.deserialize v From 0ee37c4928ed3caa4d59baf466d6f9d0a25e0f5d Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Tue, 19 Apr 2022 14:06:45 -0400 Subject: [PATCH 11/12] Extract HSTS string setting to relevant module --- fsharp-backend/src/BwdServer/Server.fs | 5 +---- fsharp-backend/src/LibService/HSTS.fs | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index 45916bd990..8bf80ecdc6 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -423,10 +423,7 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = (task { let executionID = LibService.Telemetry.executionID () ctx.Items[ "executionID" ] <- executionID - setHeader - ctx - "Strict-Transport-Security" - "max-age=31536000; includeSubDomains; preload" + setHeader ctx "Strict-Transport-Security" LibService.HSTS.stringConfig setHeader ctx "x-darklang-execution-id" (string executionID) setHeader ctx "Server" "darklang" diff --git a/fsharp-backend/src/LibService/HSTS.fs b/fsharp-backend/src/LibService/HSTS.fs index 5ea0054117..814278d96d 100644 --- a/fsharp-backend/src/LibService/HSTS.fs +++ b/fsharp-backend/src/LibService/HSTS.fs @@ -3,7 +3,11 @@ module LibService.HSTS open System open Microsoft.AspNetCore.HttpsPolicy +// If you update these, please ensure the below match each other + let setConfig (options : HstsOptions) = options.Preload <- true options.IncludeSubDomains <- true options.MaxAge <- TimeSpan.FromDays 365 + +let stringConfig = "max-age=31536000; includeSubDomains; preload" From 2894999b31249331f1dac984172efb0904944305 Mon Sep 17 00:00:00 2001 From: Stachu Korick Date: Wed, 20 Apr 2022 13:32:49 -0400 Subject: [PATCH 12/12] Provide additional commentary on BwdServer HSTS setup --- fsharp-backend/src/BwdServer/Server.fs | 6 ++++++ fsharp-backend/src/LibService/HSTS.fs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/fsharp-backend/src/BwdServer/Server.fs b/fsharp-backend/src/BwdServer/Server.fs index 8bf80ecdc6..3756dc6933 100644 --- a/fsharp-backend/src/BwdServer/Server.fs +++ b/fsharp-backend/src/BwdServer/Server.fs @@ -423,7 +423,13 @@ let configureApp (healthCheckPort : int) (app : IApplicationBuilder) = (task { let executionID = LibService.Telemetry.executionID () ctx.Items[ "executionID" ] <- executionID + // The traditional methods of using `UseHsts` and `AddHsts` within BwdServer + // were ineffective. Somehow, the Strict-Transport-Security header was not + // included in HTTP Reponses as a result of these efforts. Here, we manually + // work around this by setting it manually. + // CLEANUP: replace this with the more additional approach, if possible setHeader ctx "Strict-Transport-Security" LibService.HSTS.stringConfig + setHeader ctx "x-darklang-execution-id" (string executionID) setHeader ctx "Server" "darklang" diff --git a/fsharp-backend/src/LibService/HSTS.fs b/fsharp-backend/src/LibService/HSTS.fs index 814278d96d..2e8a7159c0 100644 --- a/fsharp-backend/src/LibService/HSTS.fs +++ b/fsharp-backend/src/LibService/HSTS.fs @@ -3,6 +3,12 @@ module LibService.HSTS open System open Microsoft.AspNetCore.HttpsPolicy +// The traditional methods of using `UseHsts` and `AddHsts` within BwdServer +// were ineffective. Somehow, the Strict-Transport-Security header was not +// included in HTTP Reponses as a result of these efforts. Here, we manually +// work around this by setting it manually. +// CLEANUP: replace this with the more additional approach, somehow + // If you update these, please ensure the below match each other let setConfig (options : HstsOptions) =