safsaf/safsaf/handler-wrappers/security-headers.scm
Christopher Baines 5b0e6397dc
All checks were successful
/ test (push) Successful in 9s
Initial commit
Safsaf is a Guile web framework, written using Claude Code running
Claude Opus 4.6, based off of the Guix Data Service, Nar Herder and
Guix Build Coordinator codebases.
2026-04-13 14:24:19 +03:00

66 lines
2.9 KiB
Scheme

;; Safsaf, a Guile web framework
;; Copyright (C) 2026 Christopher Baines <mail@cbaines.net>
;; This program is free software: you can redistribute it and/or
;; modify it under the terms of the GNU Lesser General Public License
;; as published by the Free Software Foundation, either version 3 of
;; the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; Lesser General Public License for more details.
;;
;; You should have received a copy of the GNU Lesser General Public
;; License along with this program. If not, see
;; <https://www.gnu.org/licenses/>.
(define-module (safsaf handler-wrappers security-headers)
#:use-module (srfi srfi-71)
#:use-module (web response)
#:use-module (safsaf response-helpers)
#:export (security-headers-handler-wrapper))
(define* (security-headers-handler-wrapper handler
#:key
(content-type-options "nosniff")
(frame-options "DENY")
(strict-transport-security #f)
(referrer-policy
"strict-origin-when-cross-origin")
(cross-origin-opener-policy #f)
(permissions-policy #f)
(content-security-policy #f)
(content-security-policy-report-only #f))
"Handler wrapper that adds security headers to every response.
All headers are optional and configurable. Pass #f to disable a header.
Defaults:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Not set by default (enable explicitly):
Strict-Transport-Security (e.g. \"max-age=63072000; includeSubDomains\")
Cross-Origin-Opener-Policy (e.g. \"same-origin\")
Permissions-Policy (e.g. \"camera=(), microphone=()\")
Content-Security-Policy (e.g. \"default-src 'self'; script-src 'self'\")
Content-Security-Policy-Report-Only — same syntax, for testing policies
without enforcing them"
(let ((security-headers
(filter cdr
`((x-content-type-options . ,content-type-options)
(x-frame-options . ,frame-options)
(strict-transport-security . ,strict-transport-security)
(referrer-policy . ,referrer-policy)
(cross-origin-opener-policy . ,cross-origin-opener-policy)
(permissions-policy . ,permissions-policy)
(content-security-policy . ,content-security-policy)
(content-security-policy-report-only
. ,content-security-policy-report-only)))))
(lambda (request body-port)
(let ((response body (handler request body-port)))
(values (build-response/inherit response
#:headers (append (response-headers response)
security-headers))
body)))))