nickva commented on a change in pull request #3724:
URL: https://github.com/apache/couchdb/pull/3724#discussion_r701467139
##########
File path: src/chttpd/test/eunit/chttpd_csp_tests.erl
##########
@@ -12,70 +12,257 @@
-module(chttpd_csp_tests).
+-include_lib("couch/include/couch_db.hrl").
-include_lib("couch/include/couch_eunit.hrl").
-
-setup() ->
- ok = config:set("csp", "enable", "true", false),
- Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
- Port = mochiweb_socket_server:get(chttpd, port),
- lists:concat(["http://", Addr, ":", Port, "/_utils/"]).
-
-teardown(_) ->
- ok.
-
-
+-define(ADM_USER, "adm_user").
+-define(ADM_PASS, "adm_pass").
+-define(ADM, {?ADM_USER, ?ADM_PASS}).
+-define(ACC_USER, "acc").
+-define(ACC_PASS, "pass").
+-define(ACC, {?ACC_USER, ?ACC_PASS}).
+-define(DOC1, "doc1").
+-define(DDOC1, "_design/ddoc1").
+-define(DDOC1_PATH_ENC, "_design%2Fddoc1").
+-define(LDOC1, "_local/ldoc1").
+-define(LDOC1_PATH_ENC, "_local%2Fldoc1").
+-define(ATT1, "att1").
+-define(VIEW1, "view1").
+-define(SHOW1, "show1").
+-define(LIST1, "list1").
+-define(SALT, <<"01234567890123456789012345678901">>).
+-define(TDEF(Name), {atom_to_list(Name), fun Name/1}).
+-define(TDEF(Name, Timeout), {atom_to_list(Name), Timeout, fun Name/1}).
+-define(TDEF_FE(Name), fun(Arg) -> {atom_to_list(Name), ?_test(Name(Arg))}
end).
+-define(TDEF_FE(Name, Timeout), fun(Arg) -> {atom_to_list(Name), {timeout,
Timeout, ?_test(Name(Arg))}} end).
csp_test_() ->
{
- "Content Security Policy tests",
+ "CSP Tests",
{
setup,
- fun chttpd_test_util:start_couch/0, fun
chttpd_test_util:stop_couch/1,
+ fun setup_all/0,
+ fun teardown_all/1,
{
foreach,
- fun setup/0, fun teardown/1,
+ fun setup/0,
+ fun cleanup/1,
[
+ ?TDEF_FE(plain_docs_not_sandboxed),
+ ?TDEF_FE(plain_ddocs_not_sandboxed),
+ ?TDEF_FE(local_docs_not_sandboxed),
+ ?TDEF_FE(sandbox_doc_attachments),
+ ?TDEF_FE(sandbox_ddoc_attachments),
+ ?TDEF_FE(sandbox_shows),
+ ?TDEF_FE(sandbox_lists),
fun should_not_return_any_csp_headers_when_disabled/1,
+ fun should_apply_default_policy_with_legacy_config/1,
fun should_apply_default_policy/1,
- fun should_return_custom_policy/1,
- fun should_only_enable_csp_when_true/1
+ fun should_return_custom_policy/1
]
}
}
}.
+plain_docs_not_sandboxed(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ Url = DbUrl ++ "/" ++ ?DOC1,
+ ?assertEqual({200, false}, req(get, ?ACC, Url)),
+ config:set("csp", "attachments_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+plain_ddocs_not_sandboxed(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ Url = DbUrl ++ "/" ++ ?DDOC1,
+ ?assertEqual({200, false}, req(get, ?ACC, Url)),
+ config:set("csp", "attachments_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+local_docs_not_sandboxed(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ Url = DbUrl ++ "/" ++ ?LDOC1,
+ ?assertEqual({200, false}, req(get, ?ACC, Url)),
+ config:set("csp", "attachments_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+sandbox_doc_attachments(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ Url = DbUrl ++ "/" ++ ?DOC1 ++ "/" ++ ?ATT1,
+ ?assertEqual({200, true}, req(get, ?ACC, Url)),
+ config:set("csp", "attachments_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+sandbox_ddoc_attachments(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ Url = DbUrl ++ "/" ++ ?DDOC1 ++ "/" ++ ?ATT1,
+ ?assertEqual({200, true}, req(get, ?ACC, Url)),
+ config:set("csp", "attachments_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+sandbox_shows(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ DDocUrl = DbUrl ++ "/" ++ ?DDOC1,
+ Url = DDocUrl ++ "/_show/" ++ ?SHOW1 ++ "/" ++ ?DOC1,
+ ?assertEqual({200, true}, req(get, ?ACC, Url)),
+ config:set("csp", "showlist_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
-should_not_return_any_csp_headers_when_disabled(Url) ->
+sandbox_lists(DbName) ->
+ DbUrl = base_url() ++ "/" ++ DbName,
+ DDocUrl = DbUrl ++ "/" ++ ?DDOC1,
+ Url = DDocUrl ++ "/_list/" ++ ?LIST1 ++ "/" ++ ?VIEW1,
+ ?assertEqual({200, true}, req(get, ?ACC, Url)),
+ config:set("csp", "showlist_enable", "false", false),
+ ?assertEqual({200, false}, req(get, ?ACC, Url)).
+
+
+should_not_return_any_csp_headers_when_disabled(_DbName) ->
?_assertEqual(undefined,
begin
+ ok = config:set("csp", "utils_enable", "false", false),
ok = config:set("csp", "enable", "false", false),
- {ok, _, Headers, _} = test_request:get(Url),
+ {ok, _, Headers, _} = test_request:get(base_url() ++ "/_utils/"),
proplists:get_value("Content-Security-Policy", Headers)
end).
-should_apply_default_policy(Url) ->
+should_apply_default_policy(_DbName) ->
?_assertEqual(
"child-src 'self' data: blob:; default-src 'self'; img-src 'self'
data:; font-src 'self'; "
"script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
begin
- {ok, _, Headers, _} = test_request:get(Url),
+ {ok, _, Headers, _} = test_request:get(base_url() ++ "/_utils/"),
proplists:get_value("Content-Security-Policy", Headers)
end).
-should_return_custom_policy(Url) ->
- ?_assertEqual("default-src 'http://example.com';",
+should_apply_default_policy_with_legacy_config(_DbName) ->
+ ?_assertEqual(
+ "child-src 'self' data: blob:; default-src 'self'; img-src 'self'
data:; font-src 'self'; "
+ "script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline';",
begin
- ok = config:set("csp", "header_value",
- "default-src 'http://example.com';", false),
- {ok, _, Headers, _} = test_request:get(Url),
+ ok = config:set("csp", "utils_enable", "false", false),
+ ok = config:set("csp", "enable", "true", false),
+ {ok, _, Headers, _} = test_request:get(base_url() ++ "/_utils/"),
proplists:get_value("Content-Security-Policy", Headers)
end).
-should_only_enable_csp_when_true(Url) ->
- ?_assertEqual(undefined,
+should_return_custom_policy(_DbName) ->
+ ?_assertEqual("default-src 'http://example.com';",
begin
- ok = config:set("csp", "enable", "tru", false),
- {ok, _, Headers, _} = test_request:get(Url),
+ ok = config:set("csp", "utils_header_value",
+ "default-src 'http://example.com';", false),
+ {ok, _, Headers, _} = test_request:get(base_url() ++ "/_utils/"),
proplists:get_value("Content-Security-Policy", Headers)
end).
+
+
+% Utility functions
+
+setup_all() ->
+ Ctx = test_util:start_couch([chttpd]),
+ Hashed = couch_passwords:hash_admin_password(?ADM_PASS),
+ config:set("admins", ?ADM_USER, ?b2l(Hashed), false),
+ config:set("log", "level", "debug", false),
+ Ctx.
+
+teardown_all(Ctx) ->
+ test_util:stop_couch(Ctx).
+
+setup() ->
+ UsersDb = ?b2l(?tempdb()),
+ config:set("chttpd_auth", "authentication_db", UsersDb, false),
+ UsersDbUrl = base_url() ++ "/" ++ UsersDb,
+ {201, _} = req(put, ?ADM, UsersDbUrl),
+ % Since we're dealing with the auth cache and ets_lru, it's best to just
+ % restart the whole application.
+ application:stop(chttpd),
+ ok = application:start(chttpd, permanent),
+ ok = create_user(UsersDb, <<?ACC_USER>>, <<?ACC_PASS>>, []),
+ DbName = ?b2l(?tempdb()),
+ DbUrl = base_url() ++ "/" ++ DbName,
+ {201, _} = req(put, ?ADM, DbUrl),
+ ok = create_doc(?ACC, DbName, #{
+ <<"_id">> => <<?DOC1>>,
+ <<"_attachments">> => #{
+ <<?ATT1>> => #{
+ <<"data">> => base64:encode(<<"att1_data">>)
+ }
+ }
+ }),
+ ok = create_doc(?ADM, DbName, #{
+ <<"_id">> => <<?DDOC1>>,
+ <<"_attachments">> => #{
+ <<?ATT1>> => #{
+ <<"data">> => base64:encode(<<"att1_data">>)
+ }
+ },
+ <<"views">> => #{
+ <<?VIEW1>> => #{
+ <<"map">> => <<"function(doc) {emit(doc._id, doc._rev)}">>
+ }
+ },
+ <<"shows">> => #{
+ <<?SHOW1>> => <<"function(doc, req) {return '<h1>show1!</h1>';}">>
+ },
+ <<"lists">> => #{
+ <<?LIST1>> => <<"function(head, req) {",
+ "var row;",
+ "while(row = getRow()){ send(row.key); };",
+ "}">>
+ }
+ }),
+ ok = create_doc(?ACC, DbName, #{<<"_id">> => <<?LDOC1>>}),
+ DbName.
+
+cleanup(DbName) ->
+ config:set("csp", "utils_enable", "true", false),
Review comment:
If we're returning to defaults, should we delete the values?
`config:delete("csp", "utils_enable", _Persist=false)`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]