All checks were successful
/ test (push) Successful in 9s
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.
66 lines
2.9 KiB
Scheme
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)))))
|