-module(plume@content_security_policy).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/plume/content_security_policy.gleam").
-export([to_string/1]).
-export_type([content_security_policy/0, directive/0, source/0, trusted_types_sink/0, trusted_types_policy/0, sandbox_token/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Content-Security-Policy (CSP)\n"
"\n"
" This response header lets sites declare which resources the browser is\n"
" allowed to load for a given page, mitigating cross-site scripting (XSS)\n"
" and data-injection attacks. `plume.default()` ships a sensible starter\n"
" policy.\n"
"\n"
" Most directives expect at least one source. Passing an empty list (e.g.\n"
" `DefaultSrc([])`) renders an incomplete directive — omit it entirely\n"
" instead. `Sandbox([])` is the exception; an empty token list applies the\n"
" maximum restrictions.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" Policy([\n"
" DefaultSrc([Self]),\n"
" ScriptSrc([Self]),\n"
" ImgSrc([Self, Scheme(\"data\")]),\n"
" StyleSrc([Self, UnsafeInline]),\n"
" ])\n"
" ```\n"
"\n"
" See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy).\n"
).
-type content_security_policy() :: {policy, list(directive())}.
-type directive() :: {default_src, list(source())} |
{script_src, list(source())} |
{script_src_attr, list(source())} |
{script_src_elem, list(source())} |
{style_src, list(source())} |
{style_src_attr, list(source())} |
{style_src_elem, list(source())} |
{img_src, list(source())} |
{connect_src, list(source())} |
{font_src, list(source())} |
{object_src, list(source())} |
{media_src, list(source())} |
{frame_src, list(source())} |
{fenced_frame_src, list(source())} |
{frame_ancestors, list(source())} |
{child_src, list(source())} |
{manifest_src, list(source())} |
{worker_src, list(source())} |
{base_uri, list(source())} |
{form_action, list(source())} |
{sandbox, list(sandbox_token())} |
upgrade_insecure_requests |
{require_trusted_types_for, list(trusted_types_sink())} |
{trusted_types, list(trusted_types_policy())}.
-type source() :: self |
none |
unsafe_inline |
unsafe_eval |
strict_dynamic |
wasm_unsafe_eval |
unsafe_hashes |
inline_speculation_rules |
wildcard |
{host, binary()} |
{scheme, binary()} |
{nonce, binary()} |
{sha256, binary()} |
{sha384, binary()} |
{sha512, binary()}.
-type trusted_types_sink() :: script.
-type trusted_types_policy() :: {policy_name, binary()} |
allow_duplicates |
no_policy |
any_policy.
-type sandbox_token() :: allow_downloads |
allow_forms |
allow_modals |
allow_orientation_lock |
allow_pointer_lock |
allow_popups |
allow_popups_to_escape_sandbox |
allow_presentation |
allow_same_origin |
allow_scripts |
allow_top_navigation |
allow_top_navigation_by_user_activation |
allow_top_navigation_to_custom_protocols |
allow_storage_access_by_user_activation.
-file("src/plume/content_security_policy.gleam", 295).
-spec trusted_types_policy_to_string(trusted_types_policy()) -> binary().
trusted_types_policy_to_string(Policy) ->
case Policy of
{policy_name, Name} ->
Name;
allow_duplicates ->
<<"'allow-duplicates'"/utf8>>;
no_policy ->
<<"'none'"/utf8>>;
any_policy ->
<<"*"/utf8>>
end.
-file("src/plume/content_security_policy.gleam", 289).
-spec trusted_types_sink_to_string(trusted_types_sink()) -> binary().
trusted_types_sink_to_string(Sink) ->
case Sink of
script ->
<<"'script'"/utf8>>
end.
-file("src/plume/content_security_policy.gleam", 304).
-spec sandbox_token_to_string(sandbox_token()) -> binary().
sandbox_token_to_string(Token) ->
case Token of
allow_downloads ->
<<"allow-downloads"/utf8>>;
allow_forms ->
<<"allow-forms"/utf8>>;
allow_modals ->
<<"allow-modals"/utf8>>;
allow_orientation_lock ->
<<"allow-orientation-lock"/utf8>>;
allow_pointer_lock ->
<<"allow-pointer-lock"/utf8>>;
allow_popups ->
<<"allow-popups"/utf8>>;
allow_popups_to_escape_sandbox ->
<<"allow-popups-to-escape-sandbox"/utf8>>;
allow_presentation ->
<<"allow-presentation"/utf8>>;
allow_same_origin ->
<<"allow-same-origin"/utf8>>;
allow_scripts ->
<<"allow-scripts"/utf8>>;
allow_top_navigation ->
<<"allow-top-navigation"/utf8>>;
allow_top_navigation_by_user_activation ->
<<"allow-top-navigation-by-user-activation"/utf8>>;
allow_top_navigation_to_custom_protocols ->
<<"allow-top-navigation-to-custom-protocols"/utf8>>;
allow_storage_access_by_user_activation ->
<<"allow-storage-access-by-user-activation"/utf8>>
end.
-file("src/plume/content_security_policy.gleam", 265).
-spec render_tokens(binary(), list(sandbox_token())) -> binary().
render_tokens(Name, Tokens) ->
gleam@string:join(
[Name | gleam@list:map(Tokens, fun sandbox_token_to_string/1)],
<<" "/utf8>>
).
-file("src/plume/content_security_policy.gleam", 269).
-spec source_to_string(source()) -> binary().
source_to_string(Source) ->
case Source of
self ->
<<"'self'"/utf8>>;
none ->
<<"'none'"/utf8>>;
unsafe_inline ->
<<"'unsafe-inline'"/utf8>>;
unsafe_eval ->
<<"'unsafe-eval'"/utf8>>;
strict_dynamic ->
<<"'strict-dynamic'"/utf8>>;
wasm_unsafe_eval ->
<<"'wasm-unsafe-eval'"/utf8>>;
unsafe_hashes ->
<<"'unsafe-hashes'"/utf8>>;
inline_speculation_rules ->
<<"'inline-speculation-rules'"/utf8>>;
wildcard ->
<<"*"/utf8>>;
{host, Value} ->
Value;
{scheme, Value@1} ->
<<Value@1/binary, ":"/utf8>>;
{nonce, Value@2} ->
<<<<"'nonce-"/utf8, Value@2/binary>>/binary, "'"/utf8>>;
{sha256, Value@3} ->
<<<<"'sha256-"/utf8, Value@3/binary>>/binary, "'"/utf8>>;
{sha384, Value@4} ->
<<<<"'sha384-"/utf8, Value@4/binary>>/binary, "'"/utf8>>;
{sha512, Value@5} ->
<<<<"'sha512-"/utf8, Value@5/binary>>/binary, "'"/utf8>>
end.
-file("src/plume/content_security_policy.gleam", 261).
-spec render_sources(binary(), list(source())) -> binary().
render_sources(Name, Sources) ->
gleam@string:join(
[Name | gleam@list:map(Sources, fun source_to_string/1)],
<<" "/utf8>>
).
-file("src/plume/content_security_policy.gleam", 221).
-spec directive_to_string(directive()) -> binary().
directive_to_string(Directive) ->
case Directive of
{default_src, Sources} ->
render_sources(<<"default-src"/utf8>>, Sources);
{script_src, Sources@1} ->
render_sources(<<"script-src"/utf8>>, Sources@1);
{script_src_attr, Sources@2} ->
render_sources(<<"script-src-attr"/utf8>>, Sources@2);
{script_src_elem, Sources@3} ->
render_sources(<<"script-src-elem"/utf8>>, Sources@3);
{style_src, Sources@4} ->
render_sources(<<"style-src"/utf8>>, Sources@4);
{style_src_attr, Sources@5} ->
render_sources(<<"style-src-attr"/utf8>>, Sources@5);
{style_src_elem, Sources@6} ->
render_sources(<<"style-src-elem"/utf8>>, Sources@6);
{img_src, Sources@7} ->
render_sources(<<"img-src"/utf8>>, Sources@7);
{connect_src, Sources@8} ->
render_sources(<<"connect-src"/utf8>>, Sources@8);
{font_src, Sources@9} ->
render_sources(<<"font-src"/utf8>>, Sources@9);
{object_src, Sources@10} ->
render_sources(<<"object-src"/utf8>>, Sources@10);
{media_src, Sources@11} ->
render_sources(<<"media-src"/utf8>>, Sources@11);
{frame_src, Sources@12} ->
render_sources(<<"frame-src"/utf8>>, Sources@12);
{fenced_frame_src, Sources@13} ->
render_sources(<<"fenced-frame-src"/utf8>>, Sources@13);
{frame_ancestors, Sources@14} ->
render_sources(<<"frame-ancestors"/utf8>>, Sources@14);
{child_src, Sources@15} ->
render_sources(<<"child-src"/utf8>>, Sources@15);
{manifest_src, Sources@16} ->
render_sources(<<"manifest-src"/utf8>>, Sources@16);
{worker_src, Sources@17} ->
render_sources(<<"worker-src"/utf8>>, Sources@17);
{base_uri, Sources@18} ->
render_sources(<<"base-uri"/utf8>>, Sources@18);
{form_action, Sources@19} ->
render_sources(<<"form-action"/utf8>>, Sources@19);
{sandbox, Tokens} ->
render_tokens(<<"sandbox"/utf8>>, Tokens);
upgrade_insecure_requests ->
<<"upgrade-insecure-requests"/utf8>>;
{require_trusted_types_for, Sinks} ->
gleam@string:join(
[<<"require-trusted-types-for"/utf8>> |
gleam@list:map(Sinks, fun trusted_types_sink_to_string/1)],
<<" "/utf8>>
);
{trusted_types, Policies} ->
gleam@string:join(
[<<"trusted-types"/utf8>> |
gleam@list:map(
Policies,
fun trusted_types_policy_to_string/1
)],
<<" "/utf8>>
)
end.
-file("src/plume/content_security_policy.gleam", 214).
?DOC(" Encode as the `Content-Security-Policy` header value.\n").
-spec to_string(content_security_policy()) -> binary().
to_string(Value) ->
{policy, Directives} = Value,
_pipe = Directives,
_pipe@1 = gleam@list:map(_pipe, fun directive_to_string/1),
gleam@string:join(_pipe@1, <<"; "/utf8>>).