summaryrefslogtreecommitdiffstats
path: root/dom/security/test
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/security/test
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/security/test')
-rw-r--r--dom/security/test/contentverifier/browser.ini19
-rw-r--r--dom/security/test/contentverifier/browser_verify_content_about_newtab.js20
-rw-r--r--dom/security/test/contentverifier/browser_verify_content_about_newtab2.js19
-rw-r--r--dom/security/test/contentverifier/file_about_newtab.html11
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_bad.html11
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_bad_csp.html14
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_bad_csp_signature1
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_bad_signature1
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_broken_signature1
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_good_signature1
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_sri.html36
-rw-r--r--dom/security/test/contentverifier/file_about_newtab_sri_signature1
-rw-r--r--dom/security/test/contentverifier/file_contentserver.sjs261
-rw-r--r--dom/security/test/contentverifier/goodChain.pem51
-rw-r--r--dom/security/test/contentverifier/head.js210
-rw-r--r--dom/security/test/contentverifier/script.js1
-rw-r--r--dom/security/test/contentverifier/signature.derbin0 -> 103 bytes
-rw-r--r--dom/security/test/contentverifier/sk.pem9
-rw-r--r--dom/security/test/contentverifier/style.css3
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs49
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner.html121
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner.jarbin0 -> 1105 bytes
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs103
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_server.sjs179
-rw-r--r--dom/security/test/cors/mochitest.ini11
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR.html1461
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR_cache.html587
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR_origin.html174
-rw-r--r--dom/security/test/csp/browser.ini13
-rw-r--r--dom/security/test/csp/browser_manifest-src-override-default-src.js108
-rw-r--r--dom/security/test/csp/browser_test_web_manifest.js224
-rw-r--r--dom/security/test/csp/browser_test_web_manifest_mixed_content.js53
-rw-r--r--dom/security/test/csp/file_CSP.css20
-rw-r--r--dom/security/test/csp/file_CSP.sjs26
-rw-r--r--dom/security/test/csp/file_allow_https_schemes.html14
-rw-r--r--dom/security/test/csp/file_base_uri_server.sjs61
-rw-r--r--dom/security/test/csp/file_blob_data_schemes.html49
-rw-r--r--dom/security/test/csp/file_block_all_mcb.sjs76
-rw-r--r--dom/security/test/csp/file_block_all_mixed_content_frame_navigation1.html19
-rw-r--r--dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html15
-rw-r--r--dom/security/test/csp/file_bug1229639.html7
-rw-r--r--dom/security/test/csp/file_bug1229639.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug1312272.html13
-rw-r--r--dom/security/test/csp/file_bug1312272.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug1312272.js8
-rw-r--r--dom/security/test/csp/file_bug663567.xsl27
-rw-r--r--dom/security/test/csp/file_bug663567_allows.xml28
-rw-r--r--dom/security/test/csp/file_bug663567_allows.xml^headers^1
-rw-r--r--dom/security/test/csp/file_bug663567_blocks.xml28
-rw-r--r--dom/security/test/csp/file_bug663567_blocks.xml^headers^1
-rw-r--r--dom/security/test/csp/file_bug802872.html12
-rw-r--r--dom/security/test/csp/file_bug802872.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug802872.js43
-rw-r--r--dom/security/test/csp/file_bug802872.sjs7
-rw-r--r--dom/security/test/csp/file_bug836922_npolicies.html12
-rw-r--r--dom/security/test/csp/file_bug836922_npolicies.html^headers^2
-rw-r--r--dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs53
-rw-r--r--dom/security/test/csp/file_bug836922_npolicies_violation.sjs59
-rw-r--r--dom/security/test/csp/file_bug885433_allows.html38
-rw-r--r--dom/security/test/csp/file_bug885433_allows.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug885433_blocks.html37
-rw-r--r--dom/security/test/csp/file_bug885433_blocks.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164.html15
-rw-r--r--dom/security/test/csp/file_bug886164.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164_2.html14
-rw-r--r--dom/security/test/csp/file_bug886164_2.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164_3.html12
-rw-r--r--dom/security/test/csp/file_bug886164_3.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164_4.html12
-rw-r--r--dom/security/test/csp/file_bug886164_4.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164_5.html26
-rw-r--r--dom/security/test/csp/file_bug886164_5.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug886164_6.html35
-rw-r--r--dom/security/test/csp/file_bug886164_6.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug888172.html28
-rw-r--r--dom/security/test/csp/file_bug888172.sjs43
-rw-r--r--dom/security/test/csp/file_bug909029_none.html20
-rw-r--r--dom/security/test/csp/file_bug909029_none.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug909029_star.html19
-rw-r--r--dom/security/test/csp/file_bug909029_star.html^headers^1
-rw-r--r--dom/security/test/csp/file_bug910139.sjs52
-rw-r--r--dom/security/test/csp/file_bug910139.xml28
-rw-r--r--dom/security/test/csp/file_bug910139.xsl27
-rw-r--r--dom/security/test/csp/file_bug941404.html27
-rw-r--r--dom/security/test/csp/file_bug941404_xhr.html5
-rw-r--r--dom/security/test/csp/file_bug941404_xhr.html^headers^1
-rw-r--r--dom/security/test/csp/file_child-src_iframe.html61
-rw-r--r--dom/security/test/csp/file_child-src_inner_frame.html21
-rw-r--r--dom/security/test/csp/file_child-src_service_worker.html30
-rw-r--r--dom/security/test/csp/file_child-src_service_worker.js3
-rw-r--r--dom/security/test/csp/file_child-src_shared_worker-redirect.html47
-rw-r--r--dom/security/test/csp/file_child-src_shared_worker.html34
-rw-r--r--dom/security/test/csp/file_child-src_shared_worker.js8
-rw-r--r--dom/security/test/csp/file_child-src_shared_worker_data.html37
-rw-r--r--dom/security/test/csp/file_child-src_worker-redirect.html50
-rw-r--r--dom/security/test/csp/file_child-src_worker.html32
-rw-r--r--dom/security/test/csp/file_child-src_worker.js4
-rw-r--r--dom/security/test/csp/file_child-src_worker_data.html33
-rw-r--r--dom/security/test/csp/file_child_worker.js39
-rw-r--r--dom/security/test/csp/file_child_worker.js^headers^1
-rw-r--r--dom/security/test/csp/file_connect-src-fetch.html16
-rw-r--r--dom/security/test/csp/file_connect-src.html21
-rw-r--r--dom/security/test/csp/file_data-uri_blocked.html15
-rw-r--r--dom/security/test/csp/file_data-uri_blocked.html^headers^1
-rw-r--r--dom/security/test/csp/file_doccomment_meta.html28
-rw-r--r--dom/security/test/csp/file_docwrite_meta.css3
-rw-r--r--dom/security/test/csp/file_docwrite_meta.html26
-rw-r--r--dom/security/test/csp/file_docwrite_meta.js3
-rw-r--r--dom/security/test/csp/file_dual_header_testserver.sjs46
-rw-r--r--dom/security/test/csp/file_evalscript_main.html12
-rw-r--r--dom/security/test/csp/file_evalscript_main.html^headers^2
-rw-r--r--dom/security/test/csp/file_evalscript_main.js154
-rw-r--r--dom/security/test/csp/file_evalscript_main_allowed.html12
-rw-r--r--dom/security/test/csp/file_evalscript_main_allowed.html^headers^2
-rw-r--r--dom/security/test/csp/file_evalscript_main_allowed.js121
-rw-r--r--dom/security/test/csp/file_fontloader.sjs58
-rw-r--r--dom/security/test/csp/file_fontloader.woffbin0 -> 11140 bytes
-rw-r--r--dom/security/test/csp/file_form-action.html15
-rw-r--r--dom/security/test/csp/file_form_action_server.sjs33
-rw-r--r--dom/security/test/csp/file_frameancestors.sjs54
-rw-r--r--dom/security/test/csp/file_frameancestors_main.html44
-rw-r--r--dom/security/test/csp/file_frameancestors_main.js65
-rw-r--r--dom/security/test/csp/file_hash_source.html65
-rw-r--r--dom/security/test/csp/file_hash_source.html^headers^2
-rw-r--r--dom/security/test/csp/file_iframe_sandbox_document_write.html21
-rw-r--r--dom/security/test/csp/file_iframe_sandbox_srcdoc.html11
-rw-r--r--dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^1
-rw-r--r--dom/security/test/csp/file_iframe_srcdoc.sjs79
-rw-r--r--dom/security/test/csp/file_ignore_unsafe_inline.html26
-rw-r--r--dom/security/test/csp/file_ignore_unsafe_inline_multiple_policies_server.sjs56
-rw-r--r--dom/security/test/csp/file_inlinescript.html15
-rw-r--r--dom/security/test/csp/file_inlinestyle_main.html79
-rw-r--r--dom/security/test/csp/file_inlinestyle_main.html^headers^2
-rw-r--r--dom/security/test/csp/file_inlinestyle_main_allowed.html84
-rw-r--r--dom/security/test/csp/file_inlinestyle_main_allowed.html^headers^2
-rw-r--r--dom/security/test/csp/file_invalid_source_expression.html11
-rw-r--r--dom/security/test/csp/file_leading_wildcard.html11
-rw-r--r--dom/security/test/csp/file_main.html55
-rw-r--r--dom/security/test/csp/file_main.html^headers^1
-rw-r--r--dom/security/test/csp/file_main.js51
-rw-r--r--dom/security/test/csp/file_main_worker.js48
-rw-r--r--dom/security/test/csp/file_main_worker.js^headers^1
-rw-r--r--dom/security/test/csp/file_meta_element.html25
-rw-r--r--dom/security/test/csp/file_meta_header_dual.sjs98
-rw-r--r--dom/security/test/csp/file_meta_whitespace_skipping.html31
-rw-r--r--dom/security/test/csp/file_multi_policy_injection_bypass.html15
-rw-r--r--dom/security/test/csp/file_multi_policy_injection_bypass.html^headers^1
-rw-r--r--dom/security/test/csp/file_multi_policy_injection_bypass_2.html15
-rw-r--r--dom/security/test/csp/file_multi_policy_injection_bypass_2.html^headers^1
-rw-r--r--dom/security/test/csp/file_multipart_testserver.sjs50
-rw-r--r--dom/security/test/csp/file_nonce_source.html73
-rw-r--r--dom/security/test/csp/file_nonce_source.html^headers^2
-rw-r--r--dom/security/test/csp/file_null_baseuri.html21
-rw-r--r--dom/security/test/csp/file_path_matching.html10
-rw-r--r--dom/security/test/csp/file_path_matching.js1
-rw-r--r--dom/security/test/csp/file_path_matching_incl_query.html10
-rw-r--r--dom/security/test/csp/file_path_matching_redirect.html10
-rw-r--r--dom/security/test/csp/file_path_matching_redirect_server.sjs13
-rw-r--r--dom/security/test/csp/file_ping.html19
-rw-r--r--dom/security/test/csp/file_policyuri_regression_from_multipolicy.html9
-rw-r--r--dom/security/test/csp/file_policyuri_regression_from_multipolicy.html^headers^1
-rw-r--r--dom/security/test/csp/file_policyuri_regression_from_multipolicy_policy1
-rw-r--r--dom/security/test/csp/file_redirect_content.sjs38
-rw-r--r--dom/security/test/csp/file_redirect_report.sjs17
-rw-r--r--dom/security/test/csp/file_redirect_worker.sjs34
-rw-r--r--dom/security/test/csp/file_redirects_main.html37
-rw-r--r--dom/security/test/csp/file_redirects_page.sjs103
-rw-r--r--dom/security/test/csp/file_redirects_resource.sjs149
-rw-r--r--dom/security/test/csp/file_referrerdirective.html55
-rw-r--r--dom/security/test/csp/file_report.html13
-rw-r--r--dom/security/test/csp/file_report_chromescript.js54
-rw-r--r--dom/security/test/csp/file_report_for_import.css1
-rw-r--r--dom/security/test/csp/file_report_for_import.html10
-rw-r--r--dom/security/test/csp/file_report_for_import_server.sjs49
-rw-r--r--dom/security/test/csp/file_report_uri_missing_in_report_only_header.html0
-rw-r--r--dom/security/test/csp/file_report_uri_missing_in_report_only_header.html^headers^1
-rw-r--r--dom/security/test/csp/file_require_sri_meta.js1
-rw-r--r--dom/security/test/csp/file_require_sri_meta.sjs54
-rw-r--r--dom/security/test/csp/file_sandbox_1.html16
-rw-r--r--dom/security/test/csp/file_sandbox_10.html12
-rw-r--r--dom/security/test/csp/file_sandbox_11.html25
-rw-r--r--dom/security/test/csp/file_sandbox_12.html40
-rw-r--r--dom/security/test/csp/file_sandbox_13.html25
-rw-r--r--dom/security/test/csp/file_sandbox_2.html16
-rw-r--r--dom/security/test/csp/file_sandbox_3.html13
-rw-r--r--dom/security/test/csp/file_sandbox_4.html13
-rw-r--r--dom/security/test/csp/file_sandbox_5.html26
-rw-r--r--dom/security/test/csp/file_sandbox_6.html35
-rw-r--r--dom/security/test/csp/file_sandbox_7.html15
-rw-r--r--dom/security/test/csp/file_sandbox_8.html15
-rw-r--r--dom/security/test/csp/file_sandbox_9.html12
-rw-r--r--dom/security/test/csp/file_sandbox_allow_scripts.html12
-rw-r--r--dom/security/test/csp/file_sandbox_allow_scripts.html^headers^1
-rw-r--r--dom/security/test/csp/file_sandbox_fail.js4
-rw-r--r--dom/security/test/csp/file_sandbox_pass.js4
-rw-r--r--dom/security/test/csp/file_scheme_relative_sources.js1
-rw-r--r--dom/security/test/csp/file_scheme_relative_sources.sjs42
-rw-r--r--dom/security/test/csp/file_self_none_as_hostname_confusion.html11
-rw-r--r--dom/security/test/csp/file_self_none_as_hostname_confusion.html^headers^1
-rw-r--r--dom/security/test/csp/file_sendbeacon.html21
-rw-r--r--dom/security/test/csp/file_service_worker.html19
-rw-r--r--dom/security/test/csp/file_service_worker.js1
-rw-r--r--dom/security/test/csp/file_shouldprocess.html25
-rw-r--r--dom/security/test/csp/file_strict_dynamic.js1
-rw-r--r--dom/security/test/csp/file_strict_dynamic_default_src.html14
-rw-r--r--dom/security/test/csp/file_strict_dynamic_default_src.js1
-rw-r--r--dom/security/test/csp/file_strict_dynamic_js_url.html15
-rw-r--r--dom/security/test/csp/file_strict_dynamic_non_parser_inserted.html17
-rw-r--r--dom/security/test/csp/file_strict_dynamic_non_parser_inserted_inline.html16
-rw-r--r--dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write.html15
-rw-r--r--dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html15
-rw-r--r--dom/security/test/csp/file_strict_dynamic_script_events.html14
-rw-r--r--dom/security/test/csp/file_strict_dynamic_script_events_xbl.html14
-rw-r--r--dom/security/test/csp/file_strict_dynamic_script_extern.html10
-rw-r--r--dom/security/test/csp/file_strict_dynamic_script_inline.html14
-rw-r--r--dom/security/test/csp/file_strict_dynamic_unsafe_eval.html14
-rw-r--r--dom/security/test/csp/file_subframe_run_js_if_allowed.html13
-rw-r--r--dom/security/test/csp/file_subframe_run_js_if_allowed.html^headers^1
-rw-r--r--dom/security/test/csp/file_testserver.sjs57
-rw-r--r--dom/security/test/csp/file_upgrade_insecure.html78
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_cors.html49
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_cors_server.sjs62
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs54
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_meta.html79
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_referrer.sjs55
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_referrer_server.sjs56
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_reporting.html23
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs80
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_server.sjs102
-rw-r--r--dom/security/test/csp/file_upgrade_insecure_wsh.py7
-rw-r--r--dom/security/test/csp/file_web_manifest.html6
-rw-r--r--dom/security/test/csp/file_web_manifest.json1
-rw-r--r--dom/security/test/csp/file_web_manifest.json^headers^1
-rw-r--r--dom/security/test/csp/file_web_manifest_https.html4
-rw-r--r--dom/security/test/csp/file_web_manifest_https.json1
-rw-r--r--dom/security/test/csp/file_web_manifest_mixed_content.html9
-rw-r--r--dom/security/test/csp/file_web_manifest_remote.html8
-rw-r--r--dom/security/test/csp/mochitest.ini300
-rw-r--r--dom/security/test/csp/referrerdirective.sjs36
-rw-r--r--dom/security/test/csp/test_301_redirect.html74
-rw-r--r--dom/security/test/csp/test_302_redirect.html74
-rw-r--r--dom/security/test/csp/test_303_redirect.html74
-rw-r--r--dom/security/test/csp/test_307_redirect.html75
-rw-r--r--dom/security/test/csp/test_CSP.html147
-rw-r--r--dom/security/test/csp/test_allow_https_schemes.html76
-rw-r--r--dom/security/test/csp/test_base-uri.html124
-rw-r--r--dom/security/test/csp/test_blob_data_schemes.html89
-rw-r--r--dom/security/test/csp/test_block_all_mixed_content.html99
-rw-r--r--dom/security/test/csp/test_block_all_mixed_content_frame_navigation.html46
-rw-r--r--dom/security/test/csp/test_blocked_uri_in_reports.html79
-rw-r--r--dom/security/test/csp/test_bug1229639.html51
-rw-r--r--dom/security/test/csp/test_bug1242019.html51
-rw-r--r--dom/security/test/csp/test_bug1312272.html32
-rw-r--r--dom/security/test/csp/test_bug663567.html76
-rw-r--r--dom/security/test/csp/test_bug802872.html53
-rw-r--r--dom/security/test/csp/test_bug836922_npolicies.html240
-rw-r--r--dom/security/test/csp/test_bug885433.html61
-rw-r--r--dom/security/test/csp/test_bug886164.html172
-rw-r--r--dom/security/test/csp/test_bug888172.html73
-rw-r--r--dom/security/test/csp/test_bug909029.html129
-rw-r--r--dom/security/test/csp/test_bug910139.html66
-rw-r--r--dom/security/test/csp/test_bug941404.html107
-rw-r--r--dom/security/test/csp/test_child-src_iframe.html114
-rw-r--r--dom/security/test/csp/test_child-src_worker-redirect.html125
-rw-r--r--dom/security/test/csp/test_child-src_worker.html148
-rw-r--r--dom/security/test/csp/test_child-src_worker_data.html126
-rw-r--r--dom/security/test/csp/test_connect-src.html129
-rw-r--r--dom/security/test/csp/test_docwrite_meta.html86
-rw-r--r--dom/security/test/csp/test_dual_header.html66
-rw-r--r--dom/security/test/csp/test_evalscript.html59
-rw-r--r--dom/security/test/csp/test_fontloader.html98
-rw-r--r--dom/security/test/csp/test_form-action.html105
-rw-r--r--dom/security/test/csp/test_form_action_blocks_url.html76
-rw-r--r--dom/security/test/csp/test_frameancestors.html157
-rw-r--r--dom/security/test/csp/test_hash_source.html135
-rw-r--r--dom/security/test/csp/test_iframe_sandbox.html239
-rw-r--r--dom/security/test/csp/test_iframe_sandbox_srcdoc.html62
-rw-r--r--dom/security/test/csp/test_iframe_sandbox_top_1.html80
-rw-r--r--dom/security/test/csp/test_iframe_sandbox_top_1.html^headers^1
-rw-r--r--dom/security/test/csp/test_iframe_srcdoc.html140
-rw-r--r--dom/security/test/csp/test_ignore_unsafe_inline.html122
-rw-r--r--dom/security/test/csp/test_inlinescript.html123
-rw-r--r--dom/security/test/csp/test_inlinestyle.html107
-rw-r--r--dom/security/test/csp/test_invalid_source_expression.html57
-rw-r--r--dom/security/test/csp/test_leading_wildcard.html101
-rw-r--r--dom/security/test/csp/test_meta_element.html90
-rw-r--r--dom/security/test/csp/test_meta_header_dual.html137
-rw-r--r--dom/security/test/csp/test_meta_whitespace_skipping.html81
-rw-r--r--dom/security/test/csp/test_multi_policy_injection_bypass.html119
-rw-r--r--dom/security/test/csp/test_multipartchannel.html34
-rw-r--r--dom/security/test/csp/test_nonce_source.html122
-rw-r--r--dom/security/test/csp/test_null_baseuri.html67
-rw-r--r--dom/security/test/csp/test_path_matching.html115
-rw-r--r--dom/security/test/csp/test_path_matching_redirect.html89
-rw-r--r--dom/security/test/csp/test_ping.html103
-rw-r--r--dom/security/test/csp/test_policyuri_regression_from_multipolicy.html27
-rw-r--r--dom/security/test/csp/test_redirects.html137
-rw-r--r--dom/security/test/csp/test_referrerdirective.html145
-rw-r--r--dom/security/test/csp/test_report.html107
-rw-r--r--dom/security/test/csp/test_report_for_import.html112
-rw-r--r--dom/security/test/csp/test_report_uri_missing_in_report_only_header.html47
-rw-r--r--dom/security/test/csp/test_require_sri_meta.html77
-rw-r--r--dom/security/test/csp/test_sandbox.html249
-rw-r--r--dom/security/test/csp/test_sandbox_allow_scripts.html31
-rw-r--r--dom/security/test/csp/test_scheme_relative_sources.html91
-rw-r--r--dom/security/test/csp/test_self_none_as_hostname_confusion.html55
-rw-r--r--dom/security/test/csp/test_sendbeacon.html34
-rw-r--r--dom/security/test/csp/test_service_worker.html61
-rw-r--r--dom/security/test/csp/test_shouldprocess.html98
-rw-r--r--dom/security/test/csp/test_strict_dynamic.html134
-rw-r--r--dom/security/test/csp/test_strict_dynamic_default_src.html136
-rw-r--r--dom/security/test/csp/test_strict_dynamic_parser_inserted.html95
-rw-r--r--dom/security/test/csp/test_subframe_run_js_if_allowed.html33
-rw-r--r--dom/security/test/csp/test_upgrade_insecure.html181
-rw-r--r--dom/security/test/csp/test_upgrade_insecure_cors.html86
-rw-r--r--dom/security/test/csp/test_upgrade_insecure_docwrite_iframe.html54
-rw-r--r--dom/security/test/csp/test_upgrade_insecure_referrer.html85
-rw-r--r--dom/security/test/csp/test_upgrade_insecure_reporting.html69
-rw-r--r--dom/security/test/general/bug1277803.html11
-rw-r--r--dom/security/test/general/chrome.ini7
-rw-r--r--dom/security/test/general/favicon_bug1277803.icobin0 -> 1406 bytes
-rw-r--r--dom/security/test/general/file_block_script_wrong_mime_server.sjs34
-rw-r--r--dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs46
-rw-r--r--dom/security/test/general/file_nosniff_testserver.sjs60
-rw-r--r--dom/security/test/general/mochitest.ini9
-rw-r--r--dom/security/test/general/test_block_script_wrong_mime.html100
-rw-r--r--dom/security/test/general/test_bug1277803.xul99
-rw-r--r--dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html92
-rw-r--r--dom/security/test/general/test_nosniff.html118
-rw-r--r--dom/security/test/gtest/TestCSPParser.cpp1132
-rw-r--r--dom/security/test/gtest/moz.build11
-rw-r--r--dom/security/test/hsts/browser.ini19
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_allow_active.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_allow_display.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_block_active.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_block_active_css.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_block_display.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_cache-timeout.js36
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js24
-rw-r--r--dom/security/test/hsts/browser_hsts-priming_no-duplicates.js30
-rw-r--r--dom/security/test/hsts/file_1x1.pngbin0 -> 17811 bytes
-rw-r--r--dom/security/test/hsts/file_priming-top.html84
-rw-r--r--dom/security/test/hsts/file_priming.js4
-rw-r--r--dom/security/test/hsts/file_stylesheet.css0
-rw-r--r--dom/security/test/hsts/file_testserver.sjs66
-rw-r--r--dom/security/test/hsts/head.js308
-rw-r--r--dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html13
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation.html74
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html32
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html57
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html72
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html73
-rw-r--r--dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html58
-rw-r--r--dom/security/test/mixedcontentblocker/file_main.html261
-rw-r--r--dom/security/test/mixedcontentblocker/file_main_bug803225.html182
-rw-r--r--dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py7
-rw-r--r--dom/security/test/mixedcontentblocker/file_server.sjs45
-rw-r--r--dom/security/test/mixedcontentblocker/mochitest.ini23
-rw-r--r--dom/security/test/mixedcontentblocker/test_bug803225.html152
-rw-r--r--dom/security/test/mixedcontentblocker/test_frameNavigation.html127
-rw-r--r--dom/security/test/mixedcontentblocker/test_main.html187
-rw-r--r--dom/security/test/moz.build31
-rw-r--r--dom/security/test/sri/file_bug_1271796.css2
-rw-r--r--dom/security/test/sri/iframe_csp_directive_style_imports.html6
-rw-r--r--dom/security/test/sri/iframe_csp_directive_style_imports.html^headers^1
-rw-r--r--dom/security/test/sri/iframe_require-sri-for_main.html47
-rw-r--r--dom/security/test/sri/iframe_require-sri-for_main.html^headers^1
-rw-r--r--dom/security/test/sri/iframe_require-sri-for_no_csp.html5
-rw-r--r--dom/security/test/sri/iframe_script_crossdomain.html135
-rw-r--r--dom/security/test/sri/iframe_script_sameorigin.html249
-rw-r--r--dom/security/test/sri/iframe_sri_disabled.html74
-rw-r--r--dom/security/test/sri/iframe_style_crossdomain.html117
-rw-r--r--dom/security/test/sri/iframe_style_sameorigin.html164
-rw-r--r--dom/security/test/sri/mochitest.ini57
-rw-r--r--dom/security/test/sri/rsf_csp_worker.js9
-rw-r--r--dom/security/test/sri/rsf_csp_worker.js^headers^1
-rw-r--r--dom/security/test/sri/rsf_imported.js1
-rw-r--r--dom/security/test/sri/rsf_spawn_CSPd_worker.js3
-rw-r--r--dom/security/test/sri/rsf_worker.js2
-rw-r--r--dom/security/test/sri/script.js1
-rw-r--r--dom/security/test/sri/script.js^headers^1
-rw-r--r--dom/security/test/sri/script_301.js1
-rw-r--r--dom/security/test/sri/script_301.js^headers^2
-rw-r--r--dom/security/test/sri/script_302.js1
-rw-r--r--dom/security/test/sri/script_302.js^headers^2
-rw-r--r--dom/security/test/sri/script_401.js1
-rw-r--r--dom/security/test/sri/script_401.js^headers^2
-rw-r--r--dom/security/test/sri/script_crossdomain1.js4
-rw-r--r--dom/security/test/sri/script_crossdomain1.js^headers^1
-rw-r--r--dom/security/test/sri/script_crossdomain2.js5
-rw-r--r--dom/security/test/sri/script_crossdomain3.js1
-rw-r--r--dom/security/test/sri/script_crossdomain3.js^headers^1
-rw-r--r--dom/security/test/sri/script_crossdomain4.js1
-rw-r--r--dom/security/test/sri/script_crossdomain4.js^headers^1
-rw-r--r--dom/security/test/sri/script_crossdomain5.js1
-rw-r--r--dom/security/test/sri/script_crossdomain5.js^headers^1
-rw-r--r--dom/security/test/sri/style1.css3
-rw-r--r--dom/security/test/sri/style1.css^headers^1
-rw-r--r--dom/security/test/sri/style2.css1
-rw-r--r--dom/security/test/sri/style3.css3
-rw-r--r--dom/security/test/sri/style4.css4
-rw-r--r--dom/security/test/sri/style4.css^headers^1
-rw-r--r--dom/security/test/sri/style5.css4
-rw-r--r--dom/security/test/sri/style6.css4
-rw-r--r--dom/security/test/sri/style6.css^headers^1
-rw-r--r--dom/security/test/sri/style_301.css3
-rw-r--r--dom/security/test/sri/style_301.css^headers^2
-rw-r--r--dom/security/test/sri/style_imported.css6
-rw-r--r--dom/security/test/sri/style_importing.css4
-rw-r--r--dom/security/test/sri/test_bug_1271796.html30
-rw-r--r--dom/security/test/sri/test_csp_directive_style_imports.html42
-rw-r--r--dom/security/test/sri/test_require-sri-for_csp_directive.html76
-rw-r--r--dom/security/test/sri/test_require-sri-for_csp_directive_disabled.html46
-rw-r--r--dom/security/test/sri/test_script_crossdomain.html18
-rw-r--r--dom/security/test/sri/test_script_sameorigin.html18
-rw-r--r--dom/security/test/sri/test_sri_disabled.html18
-rw-r--r--dom/security/test/sri/test_style_crossdomain.html18
-rw-r--r--dom/security/test/sri/test_style_sameorigin.html18
-rw-r--r--dom/security/test/unit/test_csp_reports.js231
-rw-r--r--dom/security/test/unit/test_csp_upgrade_insecure_request_header.js107
-rw-r--r--dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js47
-rw-r--r--dom/security/test/unit/xpcshell.ini7
423 files changed, 22274 insertions, 0 deletions
diff --git a/dom/security/test/contentverifier/browser.ini b/dom/security/test/contentverifier/browser.ini
new file mode 100644
index 000000000..c41c2e8e8
--- /dev/null
+++ b/dom/security/test/contentverifier/browser.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+support-files =
+ file_contentserver.sjs
+ file_about_newtab.html
+ file_about_newtab_bad.html
+ file_about_newtab_bad_csp.html
+ file_about_newtab_bad_csp_signature
+ file_about_newtab_good_signature
+ file_about_newtab_bad_signature
+ file_about_newtab_broken_signature
+ file_about_newtab_sri.html
+ file_about_newtab_sri_signature
+ goodChain.pem
+ head.js
+ script.js
+ style.css
+
+[browser_verify_content_about_newtab.js]
+[browser_verify_content_about_newtab2.js]
diff --git a/dom/security/test/contentverifier/browser_verify_content_about_newtab.js b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
new file mode 100644
index 000000000..f63726ef8
--- /dev/null
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab.js
@@ -0,0 +1,20 @@
+
+const TESTS = [
+ // { newtab (aboutURI) or regular load (url) : url,
+ // testStrings : expected strings in the loaded page }
+ { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+ { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "url" : URI_BAD_FILE_CACHED, "testStrings" : [BAD_ABOUT_STRING] },
+ { "aboutURI" : URI_BAD_FILE_CACHED, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+ { "aboutURI" : URI_SRI, "testStrings" : [
+ STYLESHEET_WITHOUT_SRI_BLOCKED,
+ STYLESHEET_WITH_SRI_LOADED,
+ SCRIPT_WITHOUT_SRI_BLOCKED,
+ SCRIPT_WITH_SRI_LOADED,
+ ]},
+ { "aboutURI" : URI_BAD_CSP, "testStrings" : [CSP_TEST_SUCCESS_STRING] },
+ { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
+];
+
+add_task(runTests);
diff --git a/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js
new file mode 100644
index 000000000..a35ff6660
--- /dev/null
+++ b/dom/security/test/contentverifier/browser_verify_content_about_newtab2.js
@@ -0,0 +1,19 @@
+
+const TESTS = [
+ // { newtab (aboutURI) or regular load (url) : url,
+ // testStrings : expected strings in the loaded page }
+ { "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
+ { "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_X5U, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_HTTP_X5U, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
+ { "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
+ { "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
+];
+
+add_task(runTests);
diff --git a/dom/security/test/contentverifier/file_about_newtab.html b/dom/security/test/contentverifier/file_about_newtab.html
new file mode 100644
index 000000000..f274743b9
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
+<head>
+ <meta charset="utf-8">
+ <title>Testpage for bug 1226928</title>
+</head>
+<body>
+ Just a fully good testpage for Bug 1226928<br/>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_bad.html b/dom/security/test/contentverifier/file_about_newtab_bad.html
new file mode 100644
index 000000000..45899f4f4
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1226928 -->
+<head>
+ <meta charset="utf-8">
+ <title>Testpage for bug 1226928</title>
+</head>
+<body>
+ Just a bad testpage for Bug 1226928<br/>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_csp.html b/dom/security/test/contentverifier/file_about_newtab_bad_csp.html
new file mode 100644
index 000000000..f8044b73f
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Testpage for CSP violation (inline script)</title>
+</head>
+<body>
+ CSP violation test succeeded.
+ <script>
+ // This inline script would override the success string if loaded.
+ document.body.innerHTML = "CSP violation test failed.";
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
new file mode 100644
index 000000000..ded42dc9f
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_csp_signature
@@ -0,0 +1 @@
+oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_bad_signature b/dom/security/test/contentverifier/file_about_newtab_bad_signature
new file mode 100644
index 000000000..73a3c1e34
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_bad_signature
@@ -0,0 +1 @@
+KirX94omQL7lKfWGhc777t8U29enDg0O0UcJLH3PRXcvWGO8KA6mmLS3yNCFnGiTjP3vNnVtm-sUkXr4ix8WTkKABkU4fEAi77sNOkLCKw40M9sDJOesmYInS_J2AuXX \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_broken_signature b/dom/security/test/contentverifier/file_about_newtab_broken_signature
new file mode 100644
index 000000000..468a167ff
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_broken_signature
@@ -0,0 +1 @@
+MGUCMFwSs3o95ukwBWXN1WbLgnpJ_uHWFiQROPm9zjrSqzlfiSMyLwJwIZzldWo_pBJtOwIxAJIfhXIiMVfl5NkFEJUUMxzu6FuxOJl5DCpG2wHLy9AhayLUzm4X4SpwZ6QBPapdTg \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_good_signature b/dom/security/test/contentverifier/file_about_newtab_good_signature
new file mode 100644
index 000000000..d826d49c3
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_good_signature
@@ -0,0 +1 @@
+HUndgHvxHNMiAe1SXoeyOOraUJCdxHqWkAYTu0Cq1KpAHcWZEVelNTvyXGbTLWj8btsmqNLAm08UlyK43q_2oO9DQfez3Fo8DhsKvm7TqgSXCkhUoxsYNanxWXhqw-Jw \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_about_newtab_sri.html b/dom/security/test/contentverifier/file_about_newtab_sri.html
new file mode 100644
index 000000000..4415c533a
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_sri.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1235572 -->
+<head>
+ <meta charset="utf-8">
+ <title>Testpage for bug 1235572</title>
+ <script>
+ function loaded(resource) {
+ document.getElementById("result").innerHTML += resource + " loaded\n";
+ }
+ function blocked(resource) {
+ document.getElementById("result").innerHTML += resource + " blocked\n";
+ }
+ </script>
+</head>
+<body>
+ Testing script loading without SRI for Bug 1235572<br/>
+ <div id="result"></div>
+
+ <!-- use css1 and css2 to make urls different to avoid the resource being cached-->
+ <link rel="stylesheet" href="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=css1"
+ onload="loaded('Stylesheet without SRI')"
+ onerror="blocked('Stylesheet without SRI')">
+ <link rel="stylesheet" href="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=css2"
+ integrity="sha384-/6Tvxh7SX39y62qePcvYoi5Vrf0lK8Ix3wJFLCYKI5KNJ5wIlCR8UsFC1OXwmwgd"
+ onload="loaded('Stylesheet with SRI')"
+ onerror="blocked('Stylesheet with SRI')">
+ <script src="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=script"
+ onload="loaded('Script without SRI')"
+ onerror="blocked('Script without SRI')"></script>
+ <script src="https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?resource=script"
+ integrity="sha384-zDCkvKOHXk8mM6Nk07oOGXGME17PA4+ydFw+hq0r9kgF6ZDYFWK3fLGPEy7FoOAo"
+ onload="loaded('Script with SRI')"
+ onerror="blocked('Script with SRI')"></script>
+</body>
+</html>
diff --git a/dom/security/test/contentverifier/file_about_newtab_sri_signature b/dom/security/test/contentverifier/file_about_newtab_sri_signature
new file mode 100644
index 000000000..b7ac17944
--- /dev/null
+++ b/dom/security/test/contentverifier/file_about_newtab_sri_signature
@@ -0,0 +1 @@
+yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO \ No newline at end of file
diff --git a/dom/security/test/contentverifier/file_contentserver.sjs b/dom/security/test/contentverifier/file_contentserver.sjs
new file mode 100644
index 000000000..3ea49cdde
--- /dev/null
+++ b/dom/security/test/contentverifier/file_contentserver.sjs
@@ -0,0 +1,261 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+// sjs for remote about:newtab (bug 1226928)
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.importGlobalProperties(["URLSearchParams"]);
+
+const path = "browser/dom/security/test/contentverifier/";
+
+const goodFileName = "file_about_newtab.html";
+const goodFileBase = path + goodFileName;
+const goodFile = FileUtils.getDir("TmpD", [], true);
+goodFile.append(goodFileName);
+const goodSignature = path + "file_about_newtab_good_signature";
+const goodX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
+
+const scriptFileName = "script.js";
+const cssFileName = "style.css";
+const badFile = path + "file_about_newtab_bad.html";
+const brokenSignature = path + "file_about_newtab_broken_signature";
+const badSignature = path + "file_about_newtab_bad_signature";
+const badX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=bad\"";
+const httpX5UString = "\"http://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
+
+const sriFile = path + "file_about_newtab_sri.html";
+const sriSignature = path + "file_about_newtab_sri_signature";
+
+const badCspFile = path + "file_about_newtab_bad_csp.html";
+const badCspSignature = path + "file_about_newtab_bad_csp_signature";
+
+// This cert chain is copied from
+// security/manager/ssl/tests/unit/test_content_signing/
+// using the certificates
+// * content_signing_remote_newtab_ee.pem
+// * content_signing_int.pem
+// * content_signing_root.pem
+const goodCertChainPath = path + "goodChain.pem";
+
+const tempFileNames = [goodFileName, scriptFileName, cssFileName];
+
+// we copy the file to serve as newtab to a temp directory because
+// we modify it during tests.
+setupTestFiles();
+
+function setupTestFiles() {
+ for (let fileName of tempFileNames) {
+ let tempFile = FileUtils.getDir("TmpD", [], true);
+ tempFile.append(fileName);
+ if (!tempFile.exists()) {
+ let fileIn = getFileName(path + fileName, "CurWorkD");
+ fileIn.copyTo(FileUtils.getDir("TmpD", [], true), "");
+ }
+ }
+}
+
+function getFileName(filePath, dir) {
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ let testFile =
+ Cc["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get(dir, Components.interfaces.nsILocalFile);
+ let dirs = filePath.split("/");
+ for (let i = 0; i < dirs.length; i++) {
+ testFile.append(dirs[i]);
+ }
+ return testFile;
+}
+
+function loadFile(file) {
+ // Load a file to return it.
+ let testFileStream =
+ Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ testFileStream.init(file, -1, 0, 0);
+ return NetUtil.readInputStreamToString(testFileStream,
+ testFileStream.available());
+}
+
+function appendToFile(aFile, content) {
+ try {
+ let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_APPEND |
+ FileUtils.MODE_WRONLY);
+ file.write(content, content.length);
+ file.close();
+ } catch (e) {
+ dump(">>> Error in appendToFile "+e);
+ return "Error";
+ }
+ return "Done";
+}
+
+function truncateFile(aFile, length) {
+ let fileIn = loadFile(aFile);
+ fileIn = fileIn.slice(0, -length);
+
+ try {
+ let file = FileUtils.openFileOutputStream(aFile, FileUtils.MODE_WRONLY |
+ FileUtils.MODE_TRUNCATE);
+ file.write(fileIn, fileIn.length);
+ file.close();
+ } catch (e) {
+ dump(">>> Error in truncateFile "+e);
+ return "Error";
+ }
+ return "Done";
+}
+
+function cleanupTestFiles() {
+ for (let fileName of tempFileNames) {
+ let tempFile = FileUtils.getDir("TmpD", [], true);
+ tempFile.append(fileName);
+ tempFile.remove(true);
+ }
+}
+
+/*
+ * handle requests of the following form:
+ * sig=good&key=good&file=good&header=good&cached=no to serve pages with
+ * content signatures
+ *
+ * it further handles invalidateFile=yep and validateFile=yep to change the
+ * served file
+ */
+function handleRequest(request, response) {
+ let params = new URLSearchParams(request.queryString);
+ let x5uType = params.get("x5u");
+ let signatureType = params.get("sig");
+ let fileType = params.get("file");
+ let headerType = params.get("header");
+ let cached = params.get("cached");
+ let invalidateFile = params.get("invalidateFile");
+ let validateFile = params.get("validateFile");
+ let resource = params.get("resource");
+ let x5uParam = params.get("x5u");
+
+ if (params.get("cleanup")) {
+ cleanupTestFiles();
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("Done");
+ return;
+ }
+
+ if (resource) {
+ if (resource == "script") {
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write(loadFile(getFileName(scriptFileName, "TmpD")));
+ } else { // resource == "css1" || resource == "css2"
+ response.setHeader("Content-Type", "text/css", false);
+ response.write(loadFile(getFileName(cssFileName, "TmpD")));
+ }
+ return;
+ }
+
+ // if invalidateFile is set, this doesn't actually return a newtab page
+ // but changes the served file to invalidate the signature
+ // NOTE: make sure to make the file valid again afterwards!
+ if (invalidateFile) {
+ let r = "Done";
+ for (let fileName of tempFileNames) {
+ if (appendToFile(getFileName(fileName, "TmpD"), "!") != "Done") {
+ r = "Error";
+ }
+ }
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(r);
+ return;
+ }
+
+ // if validateFile is set, this doesn't actually return a newtab page
+ // but changes the served file to make the signature valid again
+ if (validateFile) {
+ let r = "Done";
+ for (let fileName of tempFileNames) {
+ if (truncateFile(getFileName(fileName, "TmpD"), 1) != "Done") {
+ r = "Error";
+ }
+ }
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(r);
+ return;
+ }
+
+ // we have to return the certificate chain on request for the x5u parameter
+ if (x5uParam && x5uParam == "default") {
+ response.setHeader("Cache-Control", "max-age=216000", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(loadFile(getFileName(goodCertChainPath, "CurWorkD")));
+ return;
+ }
+
+ // avoid confusing cache behaviours
+ if (!cached) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ } else {
+ response.setHeader("Cache-Control", "max-age=3600", false);
+ }
+
+ // send HTML to test allowed/blocked behaviours
+ response.setHeader("Content-Type", "text/html", false);
+
+ // set signature header and key for Content-Signature header
+ /* By default a good content-signature header is returned. Any broken return
+ * value has to be indicated in the url.
+ */
+ let csHeader = "";
+ let x5uString = goodX5UString;
+ let signature = goodSignature;
+ let file = goodFile;
+ if (x5uType == "bad") {
+ x5uString = badX5UString;
+ } else if (x5uType == "http") {
+ x5uString = httpX5UString;
+ }
+ if (signatureType == "bad") {
+ signature = badSignature;
+ } else if (signatureType == "broken") {
+ signature = brokenSignature;
+ } else if (signatureType == "sri") {
+ signature = sriSignature;
+ } else if (signatureType == "bad-csp") {
+ signature = badCspSignature;
+ }
+ if (fileType == "bad") {
+ file = getFileName(badFile, "CurWorkD");
+ } else if (fileType == "sri") {
+ file = getFileName(sriFile, "CurWorkD");
+ } else if (fileType == "bad-csp") {
+ file = getFileName(badCspFile, "CurWorkD");
+ }
+
+ if (headerType == "good") {
+ // a valid content-signature header
+ csHeader = "x5u=" + x5uString + ";p384ecdsa=" +
+ loadFile(getFileName(signature, "CurWorkD"));
+ } else if (headerType == "error") {
+ // this content-signature header is missing ; before p384ecdsa
+ csHeader = "x5u=" + x5uString + "p384ecdsa=" +
+ loadFile(getFileName(signature, "CurWorkD"));
+ } else if (headerType == "errorInX5U") {
+ // this content-signature header is missing the keyid directive
+ csHeader = "x6u=" + x5uString + ";p384ecdsa=" +
+ loadFile(getFileName(signature, "CurWorkD"));
+ } else if (headerType == "errorInSignature") {
+ // this content-signature header is missing the p384ecdsa directive
+ csHeader = "x5u=" + x5uString + ";p385ecdsa=" +
+ loadFile(getFileName(signature, "CurWorkD"));
+ }
+
+ if (csHeader) {
+ response.setHeader("Content-Signature", csHeader, false);
+ }
+ let result = loadFile(file);
+
+ response.write(result);
+}
diff --git a/dom/security/test/contentverifier/goodChain.pem b/dom/security/test/contentverifier/goodChain.pem
new file mode 100644
index 000000000..d5047cfc2
--- /dev/null
+++ b/dom/security/test/contentverifier/goodChain.pem
@@ -0,0 +1,51 @@
+-----BEGIN CERTIFICATE-----
+MIICUzCCAT2gAwIBAgIUJ1BtYqWRwUsVaZCGPp9eTHIC04QwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE1MTEyODAwMDAwMFoYDzIwMTgwMjA1
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
+nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
+dKpuqc6jTjBMMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDUGA1UdEQQuMCyCKnJlbW90
+ZW5ld3RhYi5jb250ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzALBgkqhkiG9w0B
+AQsDggEBALiLck6k50ok9ahVq45P3feY1PeUXcIYZkJd8aPDYM+0kfg5+JyJBykA
+mtHWPE1QQjs7VRMfaLfu04E4UJMI2V1AON1qtgR9BQLctW85KFACg2omfiCKwJh0
+5Q8cxBFx9BpNMayqLJwHttB6oluxZFTB8CL/hfpbYpHz1bMEDCVSRP588YBrc8mV
+OLqzQK+k3ewwGvfD6SvXmTny37MxqwxdTPFJNnpqzKAsQIvz8Skic9BkA1NFk0Oq
+lsKKoiibbOCmwS9XY/laAkBaC3winuhciYAC0ImAopZ4PBCU0AOHGrNbhZXWYQxt
+uHBj34FqvIRCqgM06JCEwN0ULgix4kI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbugAwIBAgIUPcKbBQpKwTzrrlqzM+d3z5DWiNUwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
+MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADDPjITgz8joxLRW
+wpLxELKSgO/KQ6iAXztjMHq9ovT7Fy0fqBnQ1mMVFr+sBXLgtUCM45aip6PjhUXc
+zs5Dq5STg+kz7qtmAjEQvOPcyictbgdu/K7+uMhXQhlzhOgyW88Uk5vrAezNTc/e
+TvSmWp1FcgVAfaeMN/90nzD1KIHoUt7zqZIz9ub8jXPVzQNZq4vh33smZhmbdTdV
+DaHUyef5cR1VTEGB+L1qzUIQqpHmD4UkMNP1nYedWfauiQhRt6Ql3rJSCRuEvsOA
+iBTJlwai/EFwfyfHkOV2GNgv+A5wHHEjBtF5c4PCxQEL5Vw+mfZHLsDVqF3279ZY
+lQ6jQ9g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICzTCCAbegAwIBAgIUKRLJoCmk0A6PHrNc8CxFn//4BYcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUxMTI4MDAwMDAwWhgPMjAxODAyMDUwMDAw
+MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAABgMK6EyVIXTjD5qaxPO
+DWz6yREACmAQBcowKWvfhwgi27DPSXyFGDbzTPEo+7RrIcXJkVAhLouGT51fCwTZ
+zb6Sgf6ztX7VSppY9AT4utvlZKP1xQ5WhIYsMtdHCHLHIkRjeWyoBEfUx50UXNLK
+Snl+A02GKYWiX+TLLg2DPN2s7v/mm8NLMQNgUlL7KakB2FHFyPa8otPpL4llg7UJ
+iBTVQ0c3JoiVbwZaY1Z8QinfMXUrTK9egUC4BAcId1dE8glzA5RRlw1fTLWpGApt
+hUmbDnl9N2a9NhGX323ypNzIATexafipzWe7bc4u/+bFdrUqnKUoEka73pZBdHdA
+FQ==
+-----END CERTIFICATE-----
diff --git a/dom/security/test/contentverifier/head.js b/dom/security/test/contentverifier/head.js
new file mode 100644
index 000000000..d9637d18b
--- /dev/null
+++ b/dom/security/test/contentverifier/head.js
@@ -0,0 +1,210 @@
+/*
+ * Test Content-Signature for remote about:newtab
+ * - Bug 1226928 - allow about:newtab to load remote content
+ *
+ * This tests content-signature verification on remote about:newtab in the
+ * following cases (see TESTS, all failed loads display about:blank fallback):
+ * - good case (signature should verify and correct page is displayed)
+ * - reload of newtab when the siganture was invalidated after the last correct
+ * load
+ * - malformed content-signature header
+ * - malformed keyid directive
+ * - malformed p384ecdsa directive
+ * - wrong signature (this is not a siganture for the delivered document)
+ * - invalid signature (this is not even a signature)
+ * - loading a file that doesn't fit the key or signature
+ * - cache poisoning (load a malicious remote page not in newtab, subsequent
+ * newtab load has to load the fallback)
+ */
+
+const ABOUT_NEWTAB_URI = "about:newtab";
+
+const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
+const URI_GOOD = BASE + "sig=good&x5u=good&file=good&header=good";
+
+const INVALIDATE_FILE = BASE + "invalidateFile=yep";
+const VALIDATE_FILE = BASE + "validateFile=yep";
+
+const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header=";
+const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
+const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U";
+const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
+const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
+
+const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good";
+const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good";
+const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good";
+const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good";
+const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good";
+const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad";
+const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good";
+
+const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=good&file=bad&header=good&cached=true";
+
+const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
+const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
+const ABOUT_BLANK = "<head></head><body></body>";
+
+const URI_CLEANUP = BASE + "cleanup=true";
+const CLEANUP_DONE = "Done";
+
+const URI_SRI = BASE + "sig=sri&x5u=good&file=sri&header=good";
+const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
+const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
+const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
+const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
+const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
+const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
+
+const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
+
+// Needs to sync with pref "security.signed_content.CSP.default".
+const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
+
+var browser = null;
+var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
+ .getService(Ci.nsIAboutNewTabService);
+
+function pushPrefs(...aPrefs) {
+ return new Promise((resolve) => {
+ SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
+ });
+}
+
+/*
+ * run tests with input from TESTS
+ */
+function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
+ // set about:newtab location for this test if it's a newtab test
+ if (aNewTabPref) {
+ aboutNewTabService.newTabURL = aNewTabPref;
+ }
+
+ // set prefs
+ yield pushPrefs(
+ ["browser.newtabpage.remote.content-signing-test", true],
+ ["browser.newtabpage.remote", true],
+ ["security.content.signature.root_hash",
+ "CC:BE:04:87:74:B2:98:24:4A:C6:7A:71:BC:6F:DB:D6:C0:48:17:29:57:51:96:47:38:CC:24:C8:E4:F9:DD:CB"]);
+
+ if (aNewTabPref === URI_BAD_CSP) {
+ // Use stricter CSP to test CSP violation.
+ yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
+ } else {
+ // Use weaker CSP to test normal content.
+ yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
+ }
+
+ // start the test
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: aUrl,
+ },
+ function * (browser) {
+ // check if everything's set correct for testing
+ ok(Services.prefs.getBoolPref(
+ "browser.newtabpage.remote.content-signing-test"),
+ "sanity check: remote newtab signing test should be used");
+ ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
+ "sanity check: remote newtab should be used");
+ // we only check this if we really do a newtab test
+ if (aNewTabPref) {
+ ok(aboutNewTabService.overridden,
+ "sanity check: default URL for about:newtab should be overriden");
+ is(aboutNewTabService.newTabURL, aNewTabPref,
+ "sanity check: default URL for about:newtab should return the new URL");
+ }
+
+ // Every valid remote newtab page must have built-in CSP.
+ let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
+ (aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
+
+ if (shouldHaveCSP) {
+ is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
+ "Valid remote newtab page must have built-in CSP.");
+ }
+
+ yield ContentTask.spawn(
+ browser, aExpectedStrings, function * (aExpectedStrings) {
+ for (let expectedString of aExpectedStrings) {
+ ok(content.document.documentElement.innerHTML.includes(expectedString),
+ "Expect the following value in the result\n" + expectedString +
+ "\nand got " + content.document.documentElement.innerHTML);
+ }
+ });
+
+ // for good test cases we check if a reload fails if the remote page
+ // changed from valid to invalid in the meantime
+ if (reload) {
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: INVALIDATE_FILE,
+ },
+ function * (browser2) {
+ yield ContentTask.spawn(browser2, null, function * () {
+ ok(content.document.documentElement.innerHTML.includes("Done"),
+ "Expect the following value in the result\n" + "Done" +
+ "\nand got " + content.document.documentElement.innerHTML);
+ });
+ }
+ );
+
+ browser.reload();
+ yield BrowserTestUtils.browserLoaded(browser);
+
+ let expectedStrings = [ABOUT_BLANK];
+ if (aNewTabPref == URI_SRI) {
+ expectedStrings = [
+ STYLESHEET_WITHOUT_SRI_BLOCKED,
+ STYLESHEET_WITH_SRI_BLOCKED,
+ SCRIPT_WITHOUT_SRI_BLOCKED,
+ SCRIPT_WITH_SRI_BLOCKED
+ ];
+ }
+ yield ContentTask.spawn(browser, expectedStrings,
+ function * (expectedStrings) {
+ for (let expectedString of expectedStrings) {
+ ok(content.document.documentElement.innerHTML.includes(expectedString),
+ "Expect the following value in the result\n" + expectedString +
+ "\nand got " + content.document.documentElement.innerHTML);
+ }
+ }
+ );
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: VALIDATE_FILE,
+ },
+ function * (browser2) {
+ yield ContentTask.spawn(browser2, null, function * () {
+ ok(content.document.documentElement.innerHTML.includes("Done"),
+ "Expect the following value in the result\n" + "Done" +
+ "\nand got " + content.document.documentElement.innerHTML);
+ });
+ }
+ );
+ }
+ }
+ );
+}
+
+function runTests() {
+ // run tests from TESTS
+ for (let i = 0; i < TESTS.length; i++) {
+ let testCase = TESTS[i];
+ let url = "", aNewTabPref = "";
+ let reload = false;
+ var aExpectedStrings = testCase.testStrings;
+ if (testCase.aboutURI) {
+ url = ABOUT_NEWTAB_URI;
+ aNewTabPref = testCase.aboutURI;
+ if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
+ reload = true;
+ }
+ } else {
+ url = testCase.url;
+ }
+
+ yield doTest(aExpectedStrings, reload, url, aNewTabPref);
+ }
+}
diff --git a/dom/security/test/contentverifier/script.js b/dom/security/test/contentverifier/script.js
new file mode 100644
index 000000000..8fd8f96b2
--- /dev/null
+++ b/dom/security/test/contentverifier/script.js
@@ -0,0 +1 @@
+var load=true;
diff --git a/dom/security/test/contentverifier/signature.der b/dom/security/test/contentverifier/signature.der
new file mode 100644
index 000000000..011b94142
--- /dev/null
+++ b/dom/security/test/contentverifier/signature.der
Binary files differ
diff --git a/dom/security/test/contentverifier/sk.pem b/dom/security/test/contentverifier/sk.pem
new file mode 100644
index 000000000..2ed514b9f
--- /dev/null
+++ b/dom/security/test/contentverifier/sk.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAzX2TrGOr0WE92AbAl+nqnpqh25pKCLYNMTV2hJHztrkVPWOp8w0mh
+scIodK8RMpagBwYFK4EEACKhZANiAATiTcWYbt0Wg63dO7OXvpptNG0ryxv+v+Js
+JJ5Upr3pFus5fZyKxzP9NPzB+oFhL/xw3jMx7X5/vBGaQ2sJSiNlHVkqZgzYF6JQ
+4yUyiqTY7v67CyfUPA1BJg/nxOS9m3o=
+-----END EC PRIVATE KEY-----
diff --git a/dom/security/test/contentverifier/style.css b/dom/security/test/contentverifier/style.css
new file mode 100644
index 000000000..c7ab9ecff
--- /dev/null
+++ b/dom/security/test/contentverifier/style.css
@@ -0,0 +1,3 @@
+#red-text {
+ color: red;
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs
new file mode 100644
index 000000000..8ee4ddbf5
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs
@@ -0,0 +1,49 @@
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ if ("setState" in query) {
+ setState("test/dom/security/test_CrossSiteXHR_cache:secData",
+ query.setState);
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("hi");
+
+ return;
+ }
+
+ var isPreflight = request.method == "OPTIONS";
+
+ // Send response
+
+ secData =
+ eval(getState("test/dom/security/test_CrossSiteXHR_cache:secData"));
+
+ if (secData.allowOrigin)
+ response.setHeader("Access-Control-Allow-Origin", secData.allowOrigin);
+
+ if (secData.withCred)
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+
+ if (isPreflight) {
+ if (secData.allowHeaders)
+ response.setHeader("Access-Control-Allow-Headers", secData.allowHeaders);
+
+ if (secData.allowMethods)
+ response.setHeader("Access-Control-Allow-Methods", secData.allowMethods);
+
+ if (secData.cacheTime)
+ response.setHeader("Access-Control-Max-Age", secData.cacheTime.toString());
+
+ return;
+ }
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "application/xml", false);
+ response.write("<res>hello pass</res>\n");
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner.html b/dom/security/test/cors/file_CrossSiteXHR_inner.html
new file mode 100644
index 000000000..9268f0ed7
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<!--
+ NOTE! The content of this file is duplicated in file_CrossSiteXHR_inner.jar
+ and file_CrossSiteXHR_inner_data.sjs
+ Please update those files if you update this one.
+-->
+
+<html>
+<head>
+<script>
+function trimString(stringValue) {
+ return stringValue.replace(/^\s+|\s+$/g, '');
+};
+
+window.addEventListener("message", function(e) {
+
+ sendData = null;
+
+ req = eval(e.data);
+ var res = {
+ didFail: false,
+ events: [],
+ progressEvents: 0,
+ status: 0,
+ responseText: "",
+ statusText: "",
+ responseXML: null,
+ sendThrew: false
+ };
+
+ var xhr = new XMLHttpRequest();
+ for (type of ["load", "abort", "error", "loadstart", "loadend"]) {
+ xhr.addEventListener(type, function(e) {
+ res.events.push(e.type);
+ }, false);
+ }
+ xhr.addEventListener("readystatechange", function(e) {
+ res.events.push("rs" + xhr.readyState);
+ }, false);
+ xhr.addEventListener("progress", function(e) {
+ res.progressEvents++;
+ }, false);
+ if (req.uploadProgress) {
+ xhr.upload.addEventListener(req.uploadProgress, function(e) {
+ res.progressEvents++;
+ }, false);
+ }
+ xhr.onerror = function(e) {
+ res.didFail = true;
+ };
+ xhr.onloadend = function (event) {
+ res.status = xhr.status;
+ try {
+ res.statusText = xhr.statusText;
+ } catch (e) {
+ delete(res.statusText);
+ }
+ res.responseXML = xhr.responseXML ?
+ (new XMLSerializer()).serializeToString(xhr.responseXML) :
+ null;
+ res.responseText = xhr.responseText;
+
+ res.responseHeaders = {};
+ for (responseHeader in req.responseHeaders) {
+ res.responseHeaders[responseHeader] =
+ xhr.getResponseHeader(responseHeader);
+ }
+ res.allResponseHeaders = {};
+ var splitHeaders = xhr.getAllResponseHeaders().split("\r\n");
+ for (var i = 0; i < splitHeaders.length; i++) {
+ var headerValuePair = splitHeaders[i].split(":");
+ if(headerValuePair[1] != null) {
+ var headerName = trimString(headerValuePair[0]);
+ var headerValue = trimString(headerValuePair[1]);
+ res.allResponseHeaders[headerName] = headerValue;
+ }
+ }
+ post(e, res);
+ }
+
+ if (req.withCred)
+ xhr.withCredentials = true;
+ if (req.body)
+ sendData = req.body;
+
+ res.events.push("opening");
+ // Allow passign in falsy usernames/passwords so we can test them
+ try {
+ xhr.open(req.method, req.url, true,
+ ("username" in req) ? req.username : "",
+ ("password" in req) ? req.password : "aa");
+ } catch (ex) {
+ res.didFail = true;
+ post(e, res);
+ }
+
+ for (header in req.headers) {
+ xhr.setRequestHeader(header, req.headers[header]);
+ }
+
+ res.events.push("sending");
+ try {
+ xhr.send(sendData);
+ } catch (ex) {
+ res.didFail = true;
+ res.sendThrew = true;
+ post(e, res);
+ }
+
+}, false);
+
+function post(e, res) {
+ e.source.postMessage(res.toSource(), "http://mochi.test:8888");
+}
+
+</script>
+</head>
+<body>
+Inner page
+</body>
+</html>
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner.jar b/dom/security/test/cors/file_CrossSiteXHR_inner.jar
new file mode 100644
index 000000000..bdb0eb440
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner.jar
Binary files differ
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs b/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs
new file mode 100644
index 000000000..2908921e2
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs
@@ -0,0 +1,103 @@
+var data = '<!DOCTYPE HTML>\n\
+<html>\n\
+<head>\n\
+<script>\n\
+window.addEventListener("message", function(e) {\n\
+\n\
+ sendData = null;\n\
+\n\
+ req = eval(e.data);\n\
+ var res = {\n\
+ didFail: false,\n\
+ events: [],\n\
+ progressEvents: 0\n\
+ };\n\
+ \n\
+ var xhr = new XMLHttpRequest();\n\
+ for (type of ["load", "abort", "error", "loadstart", "loadend"]) {\n\
+ xhr.addEventListener(type, function(e) {\n\
+ res.events.push(e.type);\n\
+ }, false);\n\
+ }\n\
+ xhr.addEventListener("readystatechange", function(e) {\n\
+ res.events.push("rs" + xhr.readyState);\n\
+ }, false);\n\
+ xhr.addEventListener("progress", function(e) {\n\
+ res.progressEvents++;\n\
+ }, false);\n\
+ if (req.uploadProgress) {\n\
+ xhr.upload.addEventListener(req.uploadProgress, function(e) {\n\
+ res.progressEvents++;\n\
+ }, false);\n\
+ }\n\
+ xhr.onerror = function(e) {\n\
+ res.didFail = true;\n\
+ };\n\
+ xhr.onloadend = function (event) {\n\
+ res.status = xhr.status;\n\
+ try {\n\
+ res.statusText = xhr.statusText;\n\
+ } catch (e) {\n\
+ delete(res.statusText);\n\
+ }\n\
+ res.responseXML = xhr.responseXML ?\n\
+ (new XMLSerializer()).serializeToString(xhr.responseXML) :\n\
+ null;\n\
+ res.responseText = xhr.responseText;\n\
+\n\
+ res.responseHeaders = {};\n\
+ for (responseHeader in req.responseHeaders) {\n\
+ res.responseHeaders[responseHeader] =\n\
+ xhr.getResponseHeader(responseHeader);\n\
+ }\n\
+ res.allResponseHeaders = {};\n\
+ var splitHeaders = xhr.getAllResponseHeaders().split("\\r\\n");\n\
+ for (var i = 0; i < splitHeaders.length; i++) {\n\
+ var headerValuePair = splitHeaders[i].split(":");\n\
+ if(headerValuePair[1] != null){\n\
+ var headerName = trimString(headerValuePair[0]);\n\
+ var headerValue = trimString(headerValuePair[1]); \n\
+ res.allResponseHeaders[headerName] = headerValue;\n\
+ }\n\
+ }\n\
+ post(e, res);\n\
+ }\n\
+\n\
+ if (req.withCred)\n\
+ xhr.withCredentials = true;\n\
+ if (req.body)\n\
+ sendData = req.body;\n\
+\n\
+ res.events.push("opening");\n\
+ xhr.open(req.method, req.url, true);\n\
+\n\
+ for (header in req.headers) {\n\
+ xhr.setRequestHeader(header, req.headers[header]);\n\
+ }\n\
+\n\
+ res.events.push("sending");\n\
+ xhr.send(sendData);\n\
+\n\
+}, false);\n\
+\n\
+function post(e, res) {\n\
+ e.source.postMessage(res.toSource(), "*");\n\
+}\n\
+function trimString(stringValue) {\n\
+ return stringValue.replace("/^\s+|\s+$/g","");\n\
+};\n\
+\n\
+</script>\n\
+</head>\n\
+<body>\n\
+Inner page\n\
+</body>\n\
+</html>'
+
+function handleRequest(request, response)
+{
+ response.setStatusLine(null, 302, "Follow me");
+ response.setHeader("Location", "data:text/html," + escape(data));
+ response.setHeader("Content-Type", "text/plain");
+ response.write("Follow that guy!");
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
new file mode 100644
index 000000000..66d110468
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
@@ -0,0 +1,179 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var isPreflight = request.method == "OPTIONS";
+
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+ var bodyBytes = [];
+ while ((bodyAvail = bodyStream.available()) > 0)
+ Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+
+ var body = decodeURIComponent(
+ escape(String.fromCharCode.apply(null, bodyBytes)));
+
+ if (query.hop) {
+ query.hop = parseInt(query.hop, 10);
+ hops = eval(query.hops);
+ var curHop = hops[query.hop - 1];
+ query.allowOrigin = curHop.allowOrigin;
+ query.allowHeaders = curHop.allowHeaders;
+ query.allowMethods = curHop.allowMethods;
+ query.allowCred = curHop.allowCred;
+ query.noAllowPreflight = curHop.noAllowPreflight;
+ if (curHop.setCookie) {
+ query.setCookie = unescape(curHop.setCookie);
+ }
+ if (curHop.cookie) {
+ query.cookie = unescape(curHop.cookie);
+ }
+ query.noCookie = curHop.noCookie;
+ }
+
+ // Check that request was correct
+
+ if (!isPreflight && query.body && body != query.body) {
+ sendHttp500(response, "Wrong body. Expected " + query.body + " got " +
+ body);
+ return;
+ }
+
+ if (!isPreflight && "headers" in query) {
+ headers = eval(query.headers);
+ for(headerName in headers) {
+ // Content-Type is changed if there was a body
+ if (!(headerName == "Content-Type" && body) &&
+ (!request.hasHeader(headerName) ||
+ request.getHeader(headerName) != headers[headerName])) {
+ var actual = request.hasHeader(headerName) ? request.getHeader(headerName)
+ : "<missing header>";
+ sendHttp500(response,
+ "Header " + headerName + " had wrong value. Expected " +
+ headers[headerName] + " got " + actual);
+ return;
+ }
+ }
+ }
+
+ if (isPreflight && "requestHeaders" in query &&
+ request.getHeader("Access-Control-Request-Headers") != query.requestHeaders) {
+ sendHttp500(response,
+ "Access-Control-Request-Headers had wrong value. Expected " +
+ query.requestHeaders + " got " +
+ request.getHeader("Access-Control-Request-Headers"));
+ return;
+ }
+
+ if (isPreflight && "requestMethod" in query &&
+ request.getHeader("Access-Control-Request-Method") != query.requestMethod) {
+ sendHttp500(response,
+ "Access-Control-Request-Method had wrong value. Expected " +
+ query.requestMethod + " got " +
+ request.getHeader("Access-Control-Request-Method"));
+ return;
+ }
+
+ if ("origin" in query && request.getHeader("Origin") != query.origin) {
+ sendHttp500(response,
+ "Origin had wrong value. Expected " + query.origin + " got " +
+ request.getHeader("Origin"));
+ return;
+ }
+
+ if ("cookie" in query) {
+ cookies = {};
+ request.getHeader("Cookie").split(/ *; */).forEach(function (val) {
+ var [name, value] = val.split('=');
+ cookies[name] = unescape(value);
+ });
+
+ query.cookie.split(",").forEach(function (val) {
+ var [name, value] = val.split('=');
+ if (cookies[name] != value) {
+ sendHttp500(response,
+ "Cookie " + name + " had wrong value. Expected " + value +
+ " got " + cookies[name]);
+ return;
+ }
+ });
+ }
+
+ if (query.noCookie && request.hasHeader("Cookie")) {
+ sendHttp500(response,
+ "Got cookies when didn't expect to: " + request.getHeader("Cookie"));
+ return;
+ }
+
+ // Send response
+
+ if (!isPreflight && query.status) {
+ response.setStatusLine(null, query.status, query.statusMessage);
+ }
+ if (isPreflight && query.preflightStatus) {
+ response.setStatusLine(null, query.preflightStatus, "preflight status");
+ }
+
+ if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight))
+ response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
+
+ if (query.allowCred)
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+
+ if (query.setCookie)
+ response.setHeader("Set-Cookie", query.setCookie + "; path=/");
+
+ if (isPreflight) {
+ if (query.allowHeaders)
+ response.setHeader("Access-Control-Allow-Headers", query.allowHeaders);
+
+ if (query.allowMethods)
+ response.setHeader("Access-Control-Allow-Methods", query.allowMethods);
+ }
+ else {
+ if (query.responseHeaders) {
+ let responseHeaders = eval(query.responseHeaders);
+ for (let responseHeader in responseHeaders) {
+ response.setHeader(responseHeader, responseHeaders[responseHeader]);
+ }
+ }
+
+ if (query.exposeHeaders)
+ response.setHeader("Access-Control-Expose-Headers", query.exposeHeaders);
+ }
+
+ if (!isPreflight && query.hop && query.hop < hops.length) {
+ newURL = hops[query.hop].server +
+ "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" +
+ "hop=" + (query.hop + 1) + "&hops=" + escape(query.hops);
+ if ("headers" in query) {
+ newURL += "&headers=" + escape(query.headers);
+ }
+ response.setStatusLine(null, 307, "redirect");
+ response.setHeader("Location", newURL);
+
+ return;
+ }
+
+ // Send response body
+ if (!isPreflight && request.method != "HEAD") {
+ response.setHeader("Content-Type", "application/xml", false);
+ response.write("<res>hello pass</res>\n");
+ }
+ if (isPreflight && "preflightBody" in query) {
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(query.preflightBody);
+ }
+}
+
+function sendHttp500(response, text) {
+ response.setStatusLine(null, 500, text);
+}
diff --git a/dom/security/test/cors/mochitest.ini b/dom/security/test/cors/mochitest.ini
new file mode 100644
index 000000000..095b0d09d
--- /dev/null
+++ b/dom/security/test/cors/mochitest.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files =
+ file_CrossSiteXHR_cache_server.sjs
+ file_CrossSiteXHR_inner.html
+ file_CrossSiteXHR_inner.jar
+ file_CrossSiteXHR_inner_data.sjs
+ file_CrossSiteXHR_server.sjs
+
+[test_CrossSiteXHR.html]
+[test_CrossSiteXHR_cache.html]
+[test_CrossSiteXHR_origin.html]
diff --git a/dom/security/test/cors/test_CrossSiteXHR.html b/dom/security/test/cors/test_CrossSiteXHR.html
new file mode 100644
index 000000000..b3cda3b87
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -0,0 +1,1461 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="initTest()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+const runPreflightTests = 1;
+const runCookieTests = 1;
+const runRedirectTests = 1;
+
+var gen;
+
+function initTest() {
+ SimpleTest.waitForExplicitFinish();
+ // Allow all cookies, then do the actual test initialization
+ SpecialPowers.pushPrefEnv({"set": [["network.cookie.cookieBehavior", 0]]}, initTestCallback);
+}
+
+function initTestCallback() {
+ window.addEventListener("message", function(e) {
+ gen.send(e.data);
+ }, false);
+
+ gen = runTest();
+
+ gen.next()
+}
+
+function runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ // Test preflight-less requests
+ basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
+ baseURL = "http://mochi.test:8888" + basePath;
+
+ // Test preflighted requests
+ loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ tests = [// Plain request
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ },
+
+ // undefined username
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined
+ },
+
+ // undefined username and password
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined,
+ password: undefined
+ },
+
+ // nonempty username
+ { pass: 0,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: "user",
+ },
+
+ // nonempty password
+ // XXXbz this passes for now, because we ignore passwords
+ // without usernames in most cases.
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ password: "password",
+ },
+
+ // Default allowed headers
+ { pass: 1,
+ method: "GET",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // Custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "X-My-Header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
+ allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my%-header": "myValue" },
+ allowHeaders: "x-my%-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header z",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-he(ader",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "myheader": "" },
+ allowMethods: "myheader",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ allowHeaders: "User-Agent",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ },
+
+ // Multiple custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header, second-header, third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header,second-header,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header ,second-header ,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header , second-header , third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: ", x-my-header, , ,, second-header, , ",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: "x-my-header, second-header, unused-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "secondValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "",
+ "y-my-header": "" },
+ allowHeaders: "x-my-header",
+ },
+
+ // HEAD requests
+ { pass: 1,
+ method: "HEAD",
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with safe headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with custom headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+
+ // POST tests
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ },
+ { pass: 1,
+ method: "POST",
+ noAllowPreflight: 1,
+ },
+
+ // POST with standard headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "multipart/form-data" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 0,
+ method: "POST",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // POST with custom headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Accept": "foo/bar",
+ "Accept-Language": "sv-SE",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, content-type",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, $_%",
+ },
+
+ // Other methods
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowHeaders: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, PUT, DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, DELETE, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE, POST, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST ,PUT ,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST,PUT,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST , PUT , DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: " ,, PUT ,, , , DELETE , ,",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETEZ",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PUT Z",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PU(T",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT Z, DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PU(T, DELETE",
+ },
+ { pass: 0,
+ method: "MYMETHOD",
+ allowMethods: "myMethod",
+ },
+ { pass: 0,
+ method: "PUT",
+ allowMethods: "put",
+ },
+
+ // Progress events
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ noAllowPreflight: 1,
+ },
+
+ // Status messages
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 404,
+ statusMessage: "nothin' here",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 401,
+ statusMessage: "no can do",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ allowHeaders: "content-type",
+ status: 500,
+ statusMessage: "server boo",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 200,
+ statusMessage: "Yes!!",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 400
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 200
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 204
+ },
+
+ // exposed headers
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: ["x-my-header"],
+ },
+ { pass: 0,
+ method: "GET",
+ origin: "http://invalid",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header y",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "y x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-header z",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-hea(er",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header",
+ "y-my-header": "y header" },
+ exposeHeaders: " , ,,y-my-header,z-my-header, ",
+ expectedResponseHeaders: ["y-my-header"],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "Cache-Control": "cacheControl header",
+ "Content-Language": "contentLanguage header",
+ "Expires":"expires header",
+ "Last-Modified":"lastModified header",
+ "Pragma":"pragma header",
+ "Unexpected":"unexpected header" },
+ expectedResponseHeaders: ["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"],
+ },
+ // Check that sending a body in the OPTIONS response works
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ preflightBody: "I'm a preflight response body",
+ },
+ ];
+
+ if (!runPreflightTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ var req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ uploadProgress: test.uploadProgress,
+ body: test.body,
+ responseHeaders: test.responseHeaders,
+ };
+
+ if (test.pass) {
+ req.url += "&origin=" + escape(origin) +
+ "&requestMethod=" + test.method;
+ }
+
+ if ("username" in test) {
+ req.username = test.username;
+ }
+
+ if ("password" in test) {
+ req.password = test.password;
+ }
+
+ if (test.noAllowPreflight)
+ req.url += "&noAllowPreflight";
+
+ if (test.pass && "headers" in test) {
+ function isUnsafeHeader(name) {
+ lName = name.toLowerCase();
+ return lName != "accept" &&
+ lName != "accept-language" &&
+ (lName != "content-type" ||
+ ["text/plain",
+ "multipart/form-data",
+ "application/x-www-form-urlencoded"]
+ .indexOf(test.headers[name].toLowerCase()) == -1);
+ }
+ req.url += "&headers=" + escape(test.headers.toSource());
+ reqHeaders =
+ escape(Object.keys(test.headers)
+ .filter(isUnsafeHeader)
+ .map(String.toLowerCase)
+ .sort()
+ .join(","));
+ req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
+ }
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ if (test.status) {
+ req.url += "&status=" + test.status;
+ req.url += "&statusMessage=" + escape(test.statusMessage);
+ }
+ if (test.preflightStatus)
+ req.url += "&preflightStatus=" + test.preflightStatus;
+ if (test.responseHeaders)
+ req.url += "&responseHeaders=" + escape(test.responseHeaders.toSource());
+ if (test.exposeHeaders)
+ req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
+ if (test.preflightBody)
+ req.url += "&preflightBody=" + escape(test.preflightBody);
+
+ loaderWindow.postMessage(req.toSource(), origin);
+ res = eval(yield);
+
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + test.toSource());
+ if (test.status) {
+ is(res.status, test.status, "wrong status in test for " + test.toSource());
+ is(res.statusText, test.statusMessage, "wrong status text for " + test.toSource());
+ }
+ else {
+ is(res.status, 200, "wrong status in test for " + test.toSource());
+ is(res.statusText, "OK", "wrong status text for " + test.toSource());
+ }
+ if (test.method !== "HEAD") {
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + test.toSource());
+ }
+ else {
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,load,loadend",
+ "wrong responseText in test for " + test.toSource());
+ }
+ if (test.responseHeaders) {
+ for (header in test.responseHeaders) {
+ if (test.expectedResponseHeaders.indexOf(header) == -1) {
+ is(res.responseHeaders[header], null,
+ "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
+ test.toSource());
+ is(res.allResponseHeaders[header], undefined,
+ "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
+ test.toSource());
+ }
+ else {
+ is(res.responseHeaders[header], test.responseHeaders[header],
+ "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
+ test.toSource());
+ is(res.allResponseHeaders[header], test.responseHeaders[header],
+ "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
+ test.toSource());
+ }
+ }
+ }
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + test.toSource());
+ is(res.status, 0, "wrong status in test for " + test.toSource());
+ is(res.statusText, "", "wrong status text for " + test.toSource());
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "",
+ "wrong responseText in test for " + test.toSource());
+ if (!res.sendThrew) {
+ if (test.username) {
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + test.toSource());
+ } else {
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
+ "wrong events in test for " + test.toSource());
+ }
+ }
+ is(res.progressEvents, 0,
+ "wrong events in test for " + test.toSource());
+ if (test.responseHeaders) {
+ for (header in test.responseHeaders) {
+ is(res.responseHeaders[header], null,
+ "wrong response header (" + header + ") in test for " +
+ test.toSource());
+ }
+ }
+ }
+ }
+
+ // Test cookie behavior
+ tests = [{ pass: 1,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: 0,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ noCookie: 1,
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ noCookie: 1,
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ ];
+
+ if (!runCookieTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ if (test.allowCred)
+ req.url += "&allowCred";
+
+ if (test.setCookie)
+ req.url += "&setCookie=" + escape(test.setCookie);
+ if (test.cookie)
+ req.url += "&cookie=" + escape(test.cookie);
+ if (test.noCookie)
+ req.url += "&noCookie";
+
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+
+ loaderWindow.postMessage(req.toSource(), origin);
+
+ res = eval(yield);
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + test.toSource());
+ is(res.status, 200, "wrong status in test for " + test.toSource());
+ is(res.statusText, "OK", "wrong status text for " + test.toSource());
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + test.toSource());
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + test.toSource());
+ is(res.status, 0, "wrong status in test for " + test.toSource());
+ is(res.statusText, "", "wrong status text for " + test.toSource());
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
+ "wrong events in test for " + test.toSource());
+ is(res.progressEvents, 0,
+ "wrong events in test for " + test.toSource());
+ }
+ }
+
+ // Make sure to clear cookies to avoid affecting other tests
+ document.cookie = "a=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT"
+ is(document.cookie, "", "No cookies should be left over");
+
+
+ // Test redirects
+ is(loader.src, "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
+ is(origin, "http://example.org");
+
+ tests = [{ pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: origin
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: "*"
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "x"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+
+ // test redirects with different credentials settings
+ {
+ // Initialize by setting a cookies for same- and cross- origins.
+ pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ setCookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ setCookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ // expected fail because allow-credentials CORS header is not set
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ ];
+
+ if (!runRedirectTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: test.hops[0].server + basePath + "hop=1&hops=" +
+ escape(test.hops.toSource()),
+ method: test.method,
+ headers: test.headers,
+ body: test.body,
+ withCred: test.withCred,
+ };
+
+ if (test.pass) {
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ }
+
+ loaderWindow.postMessage(req.toSource(), origin);
+
+ res = eval(yield);
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + test.toSource());
+ is(res.status, 200, "wrong status in test for " + test.toSource());
+ is(res.statusText, "OK", "wrong status text for " + test.toSource());
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + test.toSource());
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + test.toSource());
+ is(res.status, 0, "wrong status in test for " + test.toSource());
+ is(res.statusText, "", "wrong status text for " + test.toSource());
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + test.toSource());
+ is(res.responseText, "",
+ "wrong responseText in test for " + test.toSource());
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
+ "wrong events in test for " + test.toSource());
+ is(res.progressEvents, 0,
+ "wrong progressevents in test for " + test.toSource());
+ }
+ }
+
+
+ SimpleTest.finish();
+
+ yield undefined;
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/test_CrossSiteXHR_cache.html b/dom/security/test/cors/test_CrossSiteXHR_cache.html
new file mode 100644
index 000000000..3252eff4a
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR_cache.html
@@ -0,0 +1,587 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="gen.next()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("This test needs to generate artificial pauses, hence it uses timeouts. There is no way around it, unfortunately. :(");
+
+window.addEventListener("message", function(e) {
+ gen.send(e.data);
+}, false);
+
+gen = runTest();
+
+function runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ tests = [{ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "second" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ allowHeaders: "y-my-header,x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "second" },
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 2
+ },
+ { pause: 2.1 },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "z-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "\t 3600 \t ",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "3600 3",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "asdf",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ allowHeaders: "first-header",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ allowHeaders: "second-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ allowHeaders: "third-header",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ allowHeaders: "first-header",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ allowHeaders: "second-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ allowHeaders: "third-header",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "PATCH",
+ },
+ { pass: 1,
+ method: "PATCH",
+ allowMethods: "PATCH",
+ },
+ { pass: 0,
+ method: "PATCH",
+ },
+ { pass: 1,
+ method: "PATCH",
+ allowMethods: "PATCH",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "PATCH",
+ },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "PUT",
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 2
+ },
+ { pause: 2.1 },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE, PUT",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "PUT",
+ },
+ { pass: 0,
+ method: "PATCH",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "FIRST",
+ allowMethods: "FIRST",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "SECOND",
+ allowMethods: "SECOND",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "THIRD",
+ allowMethods: "THIRD",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "SECOND",
+ },
+ { pass: 0,
+ method: "FIRST",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "FIRST",
+ allowMethods: "FIRST",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "SECOND",
+ allowMethods: "SECOND",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "THIRD",
+ allowMethods: "THIRD",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "SECOND",
+ },
+ { pass: 0,
+ method: "THIRD",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" }
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "y-my-header": "y-value" }
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" }
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { pass: 0,
+ method: "PUT",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { pass: 0,
+ method: "GET",
+ noOrigin: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "PUT"
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "DELETE",
+ headers: { "my-header": "value" },
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "GET",
+ noOrigin: 1,
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ cacheTime: 2
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: true,
+ headers: { "y-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "DELETE": "myvalue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 0,
+ method: "3600",
+ headers: { "x-my-header": "myvalue" },
+ },
+ ];
+
+ for (let i = 0; i < 110; i++) {
+ tests.push({ newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ });
+ }
+
+ baseURL = "http://mochi.test:8888/tests/dom/security/test/cors/" +
+ "file_CrossSiteXHR_cache_server.sjs?";
+ setStateURL = baseURL + "setState=";
+
+ var unique = Date.now();
+ for (test of tests) {
+ if (test.newTest) {
+ unique++;
+ continue;
+ }
+ if (test.pause) {
+ setTimeout(function() { gen.next() }, test.pause * 1000);
+ yield undefined;
+ continue;
+ }
+
+ req = {
+ url: baseURL + "c=" + unique,
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ sec = { allowOrigin: test.noOrigin ? "" : origin,
+ allowHeaders: test.allowHeaders,
+ allowMethods: test.allowMethods,
+ cacheTime: test.cacheTime,
+ withCred: test.withCred };
+ xhr = new XMLHttpRequest();
+ xhr.open("POST", setStateURL + escape(sec.toSource()), true);
+ xhr.onloadend = function() { gen.next(); }
+ xhr.send();
+ yield undefined;
+
+ loaderWindow.postMessage(req.toSource(), origin);
+
+ res = eval(yield);
+
+ testName = test.toSource() + " (index " + tests.indexOf(test) + ")";
+
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + testName);
+ is(res.status, 200, "wrong status in test for " + testName);
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + testName);
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + testName);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong events in test for " + testName);
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + testName);
+ is(res.status, 0, "wrong status in test for " + testName);
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + testName);
+ is(res.responseText, "",
+ "wrong responseText in test for " + testName);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
+ "wrong events in test for " + testName);
+ is(res.progressEvents, 0,
+ "wrong events in test for " + testName);
+ }
+ }
+
+ SimpleTest.finish();
+
+ yield undefined;
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/test_CrossSiteXHR_origin.html b/dom/security/test/cors/test_CrossSiteXHR_origin.html
new file mode 100644
index 000000000..1012ae593
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR_origin.html
@@ -0,0 +1,174 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.8">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
+
+var origins =
+ [{ server: 'http://example.org' },
+ { server: 'http://example.org:80',
+ origin: 'http://example.org'
+ },
+ { server: 'http://sub1.test1.example.org' },
+ { server: 'http://test2.example.org:8000' },
+ { server: 'http://sub1.\xe4lt.example.org:8000',
+ origin: 'http://sub1.xn--lt-uia.example.org:8000'
+ },
+ { server: 'http://sub2.\xe4lt.example.org',
+ origin: 'http://sub2.xn--lt-uia.example.org'
+ },
+ { server: 'http://ex\xe4mple.test',
+ origin: 'http://xn--exmple-cua.test'
+ },
+ { server: 'http://xn--exmple-cua.test' },
+ { server: 'http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae',
+ origin: 'http://xn--hxajbheg2az3al.xn--jxalpdlp'
+ },
+ { origin: 'http://example.org',
+ file: 'jar:http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.jar!/file_CrossSiteXHR_inner.html'
+ },
+ { origin: 'null',
+ file: 'http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs'
+ },
+ ];
+
+ //['https://example.com:443'],
+ //['https://sub1.test1.example.com:443'],
+
+window.addEventListener("message", function(e) {
+ gen.send(e.data);
+}, false);
+
+gen = runTest();
+
+function runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ // Test preflight-less requests
+ basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
+ baseURL = "http://mochi.test:8888" + basePath;
+
+ for (originEntry of origins) {
+ origin = originEntry.origin || originEntry.server;
+
+ loader.src = originEntry.file ||
+ (originEntry.server + "/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
+ yield undefined;
+
+ var isNullOrigin = origin == "null";
+
+ port = /:\d+/;
+ passTests = [
+ origin,
+ "*",
+ " \t " + origin + "\t \t",
+ "\t \t* \t ",
+ ];
+ failTests = [
+ "",
+ " ",
+ port.test(origin) ? origin.replace(port, "")
+ : origin + ":1234",
+ port.test(origin) ? origin.replace(port, ":")
+ : origin + ":",
+ origin + ".",
+ origin + "/",
+ origin + "#",
+ origin + "?",
+ origin + "\\",
+ origin + "%",
+ origin + "@",
+ origin + "/hello",
+ "foo:bar@" + origin,
+ "* " + origin,
+ origin + " " + origin,
+ "allow <" + origin + ">",
+ "<" + origin + ">",
+ "<*>",
+ origin.substr(0, 5) == "https" ? origin.replace("https", "http")
+ : origin.replace("http", "https"),
+ origin.replace("://", "://www."),
+ origin.replace("://", ":// "),
+ origin.replace(/\/[^.]+\./, "/"),
+ ];
+
+ if (isNullOrigin) {
+ passTests = ["*", "\t \t* \t ", "null"];
+ failTests = failTests.filter(function(v) { return v != origin });
+ }
+
+ for (allowOrigin of passTests) {
+ req = {
+ url: baseURL +
+ "allowOrigin=" + escape(allowOrigin) +
+ "&origin=" + escape(origin),
+ method: "GET",
+ };
+ loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin);
+
+ res = eval(yield);
+ is(res.didFail, false, "shouldn't have failed for " + allowOrigin);
+ is(res.status, 200, "wrong status for " + allowOrigin);
+ is(res.statusText, "OK", "wrong status text for " + allowOrigin);
+ is(res.responseXML,
+ "<res>hello pass</res>",
+ "wrong responseXML in test for " + allowOrigin);
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + allowOrigin);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + allowOrigin);
+ }
+
+ for (allowOrigin of failTests) {
+ req = {
+ url: baseURL + "allowOrigin=" + escape(allowOrigin),
+ method: "GET",
+ };
+ loaderWindow.postMessage(req.toSource(), isNullOrigin ? "*" : origin);
+
+ res = eval(yield);
+ is(res.didFail, true, "should have failed for " + allowOrigin);
+ is(res.responseText, "", "should have no text for " + allowOrigin);
+ is(res.status, 0, "should have no status for " + allowOrigin);
+ is(res.statusText, "", "wrong status text for " + allowOrigin);
+ is(res.responseXML, null, "should have no XML for " + allowOrigin);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,error,loadend",
+ "wrong events in test for " + allowOrigin);
+ is(res.progressEvents, 0,
+ "wrong events in test for " + allowOrigin);
+ }
+ }
+
+ SimpleTest.finish();
+
+ yield undefined;
+}
+
+addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
+ gen.next();
+ });
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/browser.ini b/dom/security/test/csp/browser.ini
new file mode 100644
index 000000000..0846e87fe
--- /dev/null
+++ b/dom/security/test/csp/browser.ini
@@ -0,0 +1,13 @@
+[DEFAULT]
+support-files =
+ !/dom/security/test/csp/file_testserver.sjs
+ !/dom/security/test/csp/file_web_manifest.html
+ !/dom/security/test/csp/file_web_manifest.json
+ !/dom/security/test/csp/file_web_manifest.json^headers^
+ !/dom/security/test/csp/file_web_manifest_https.html
+ !/dom/security/test/csp/file_web_manifest_https.json
+ !/dom/security/test/csp/file_web_manifest_mixed_content.html
+ !/dom/security/test/csp/file_web_manifest_remote.html
+[browser_test_web_manifest.js]
+[browser_test_web_manifest_mixed_content.js]
+[browser_manifest-src-override-default-src.js]
diff --git a/dom/security/test/csp/browser_manifest-src-override-default-src.js b/dom/security/test/csp/browser_manifest-src-override-default-src.js
new file mode 100644
index 000000000..0c4c7b7bc
--- /dev/null
+++ b/dom/security/test/csp/browser_manifest-src-override-default-src.js
@@ -0,0 +1,108 @@
+/*
+ * Description of the tests:
+ * Tests check that default-src can be overridden by manifest-src.
+ */
+/*globals Cu, is, ok*/
+"use strict";
+const {
+ ManifestObtainer
+} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
+const path = "/tests/dom/security/test/csp/";
+const testFile = `${path}file_web_manifest.html`;
+const mixedContentFile = `${path}file_web_manifest_mixed_content.html`;
+const server = `${path}file_testserver.sjs`;
+const defaultURL = new URL(`http://example.org${server}`);
+const mixedURL = new URL(`http://mochi.test:8888${server}`);
+const tests = [
+ // Check interaction with default-src and another origin,
+ // CSP allows fetching from example.org, so manifest should load.
+ {
+ expected: `CSP manifest-src overrides default-src of elsewhere.com`,
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("cors", "*");
+ url.searchParams.append("csp", "default-src http://elsewhere.com; manifest-src http://example.org");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+ // Check interaction with default-src none,
+ // CSP allows fetching manifest from example.org, so manifest should load.
+ {
+ expected: `CSP manifest-src overrides default-src`,
+ get tabURL() {
+ const url = new URL(mixedURL);
+ url.searchParams.append("file", mixedContentFile);
+ url.searchParams.append("cors", "http://test:80");
+ url.searchParams.append("csp", "default-src 'self'; manifest-src http://test:80");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+];
+
+//jscs:disable
+add_task(function* () {
+ //jscs:enable
+ const testPromises = tests.map((test) => {
+ const tabOptions = {
+ gBrowser,
+ url: test.tabURL,
+ skipAnimation: true,
+ };
+ return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
+ });
+ yield Promise.all(testPromises);
+});
+
+function* testObtainingManifest(aBrowser, aTest) {
+ const expectsBlocked = aTest.expected.includes("block");
+ const observer = (expectsBlocked) ? createNetObserver(aTest) : null;
+ // Expect an exception (from promise rejection) if there a content policy
+ // that is violated.
+ try {
+ const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
+ aTest.run(manifest);
+ } catch (e) {
+ const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource");
+ ok(wasBlocked,`Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`);
+ if (observer) {
+ yield observer.untilFinished;
+ }
+ }
+}
+
+// Helper object used to observe policy violations. It waits 1 seconds
+// for a response, and then times out causing its associated test to fail.
+function createNetObserver(test) {
+ let finishedTest;
+ let success = false;
+ const finished = new Promise((resolver) => {
+ finishedTest = resolver;
+ });
+ const timeoutId = setTimeout(() => {
+ if (!success) {
+ test.run("This test timed out.");
+ finishedTest();
+ }
+ }, 1000);
+ var observer = {
+ get untilFinished(){
+ return finished;
+ },
+ observe(subject, topic) {
+ SpecialPowers.removeObserver(observer, "csp-on-violate-policy");
+ test.run(topic);
+ finishedTest();
+ clearTimeout(timeoutId);
+ success = true;
+ },
+ };
+ SpecialPowers.addObserver(observer, "csp-on-violate-policy", false);
+ return observer;
+}
diff --git a/dom/security/test/csp/browser_test_web_manifest.js b/dom/security/test/csp/browser_test_web_manifest.js
new file mode 100644
index 000000000..df23770ba
--- /dev/null
+++ b/dom/security/test/csp/browser_test_web_manifest.js
@@ -0,0 +1,224 @@
+/*
+ * Description of the tests:
+ * These tests check for conformance to the CSP spec as they relate to Web Manifests.
+ *
+ * In particular, the tests check that default-src and manifest-src directives are
+ * are respected by the ManifestObtainer.
+ */
+/*globals Cu, is, ok*/
+"use strict";
+const {
+ ManifestObtainer
+} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
+const path = "/tests/dom/security/test/csp/";
+const testFile = `${path}file_web_manifest.html`;
+const remoteFile = `${path}file_web_manifest_remote.html`;
+const httpsManifest = `${path}file_web_manifest_https.html`;
+const server = `${path}file_testserver.sjs`;
+const defaultURL = new URL(`http://example.org${server}`);
+const secureURL = new URL(`https://example.com:443${server}`);
+const tests = [
+ // CSP block everything, so trying to load a manifest
+ // will result in a policy violation.
+ {
+ expected: "default-src 'none' blocks fetching manifest.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "default-src 'none'");
+ return url.href;
+ },
+ run(topic) {
+ is(topic, "csp-on-violate-policy", this.expected);
+ }
+ },
+ // CSP allows fetching only from mochi.test:8888,
+ // so trying to load a manifest from same origin
+ // triggers a CSP violation.
+ {
+ expected: "default-src mochi.test:8888 blocks manifest fetching.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "default-src mochi.test:8888");
+ return url.href;
+ },
+ run(topic) {
+ is(topic, "csp-on-violate-policy", this.expected);
+ }
+ },
+ // CSP restricts fetching to 'self', so allowing the manifest
+ // to load. The name of the manifest is then checked.
+ {
+ expected: "CSP default-src 'self' allows fetch of manifest.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "default-src 'self'");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+ // CSP only allows fetching from mochi.test:8888 and remoteFile
+ // requests a manifest from that origin, so manifest should load.
+ {
+ expected: "CSP default-src mochi.test:8888 allows fetching manifest.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", remoteFile);
+ url.searchParams.append("csp", "default-src http://mochi.test:8888");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+ // default-src blocks everything, so any attempt to
+ // fetch a manifest from another origin will trigger a
+ // policy violation.
+ {
+ expected: "default-src 'none' blocks mochi.test:8888",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", remoteFile);
+ url.searchParams.append("csp", "default-src 'none'");
+ return url.href;
+ },
+ run(topic) {
+ is(topic, "csp-on-violate-policy", this.expected);
+ }
+ },
+ // CSP allows fetching from self, so manifest should load.
+ {
+ expected: "CSP manifest-src allows self",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "manifest-src 'self'");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+ // CSP allows fetching from example.org, so manifest should load.
+ {
+ expected: "CSP manifest-src allows http://example.org",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "manifest-src http://example.org");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ }, {
+ expected: "CSP manifest-src allows mochi.test:8888",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", remoteFile);
+ url.searchParams.append("cors", "*");
+ url.searchParams.append("csp", "default-src *; manifest-src http://mochi.test:8888");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+ // CSP restricts fetching to mochi.test:8888, but the test
+ // file is at example.org. Hence, a policy violation is
+ // triggered.
+ {
+ expected: "CSP blocks manifest fetching from example.org.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", testFile);
+ url.searchParams.append("csp", "manifest-src mochi.test:8888");
+ return url.href;
+ },
+ run(topic) {
+ is(topic, "csp-on-violate-policy", this.expected);
+ }
+ },
+ // CSP is set to only allow manifest to be loaded from same origin,
+ // but the remote file attempts to load from a different origin. Thus
+ // this causes a CSP violation.
+ {
+ expected: "CSP manifest-src 'self' blocks cross-origin fetch.",
+ get tabURL() {
+ const url = new URL(defaultURL);
+ url.searchParams.append("file", remoteFile);
+ url.searchParams.append("csp", "manifest-src 'self'");
+ return url.href;
+ },
+ run(topic) {
+ is(topic, "csp-on-violate-policy", this.expected);
+ }
+ },
+ // CSP allows fetching over TLS from example.org, so manifest should load.
+ {
+ expected: "CSP manifest-src allows example.com over TLS",
+ get tabURL() {
+ // secureURL loads https://example.com:443
+ // and gets manifest from https://example.org:443
+ const url = new URL(secureURL);
+ url.searchParams.append("file", httpsManifest);
+ url.searchParams.append("cors", "*");
+ url.searchParams.append("csp", "manifest-src https://example.com:443");
+ return url.href;
+ },
+ run(manifest) {
+ is(manifest.name, "loaded", this.expected);
+ }
+ },
+];
+
+//jscs:disable
+add_task(function* () {
+ //jscs:enable
+ const testPromises = tests.map((test) => {
+ const tabOptions = {
+ gBrowser,
+ url: test.tabURL,
+ skipAnimation: true,
+ };
+ return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
+ });
+ yield Promise.all(testPromises);
+});
+
+function* testObtainingManifest(aBrowser, aTest) {
+ const waitForObserver = waitForNetObserver(aTest);
+ // Expect an exception (from promise rejection) if there a content policy
+ // that is violated.
+ try {
+ const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
+ aTest.run(manifest);
+ } catch (e) {
+ const wasBlocked = e.message.includes("NetworkError when attempting to fetch resource");
+ ok(wasBlocked, `Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`);
+ } finally {
+ yield waitForObserver;
+ }
+}
+
+// Helper object used to observe policy violations when blocking is expected.
+function waitForNetObserver(aTest) {
+ return new Promise((resolve) => {
+ // We don't need to wait for violation, so just resolve
+ if (!aTest.expected.includes("block")){
+ return resolve();
+ }
+ const observer = {
+ observe(subject, topic) {
+ SpecialPowers.removeObserver(observer, "csp-on-violate-policy");
+ aTest.run(topic);
+ resolve();
+ },
+ };
+ SpecialPowers.addObserver(observer, "csp-on-violate-policy", false);
+ });
+}
diff --git a/dom/security/test/csp/browser_test_web_manifest_mixed_content.js b/dom/security/test/csp/browser_test_web_manifest_mixed_content.js
new file mode 100644
index 000000000..9238acbcd
--- /dev/null
+++ b/dom/security/test/csp/browser_test_web_manifest_mixed_content.js
@@ -0,0 +1,53 @@
+/*
+ * Description of the test:
+ * Check that mixed content blocker works prevents fetches of
+ * mixed content manifests.
+ */
+/*globals Cu, ok*/
+"use strict";
+const {
+ ManifestObtainer
+} = Cu.import("resource://gre/modules/ManifestObtainer.jsm", {});
+const path = "/tests/dom/security/test/csp/";
+const mixedContent = `${path}file_web_manifest_mixed_content.html`;
+const server = `${path}file_testserver.sjs`;
+const secureURL = new URL(`https://example.com${server}`);
+const tests = [
+ // Trying to load mixed content in file_web_manifest_mixed_content.html
+ // needs to result in an error.
+ {
+ expected: "Mixed Content Blocker prevents fetching manifest.",
+ get tabURL() {
+ const url = new URL(secureURL);
+ url.searchParams.append("file", mixedContent);
+ return url.href;
+ },
+ run(error) {
+ // Check reason for error.
+ const check = /NetworkError when attempting to fetch resource/.test(error.message);
+ ok(check, this.expected);
+ }
+ }
+];
+
+//jscs:disable
+add_task(function* () {
+ //jscs:enable
+ const testPromises = tests.map((test) => {
+ const tabOptions = {
+ gBrowser,
+ url: test.tabURL,
+ skipAnimation: true,
+ };
+ return BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test));
+ });
+ yield Promise.all(testPromises);
+});
+
+function* testObtainingManifest(aBrowser, aTest) {
+ try {
+ yield ManifestObtainer.browserObtainManifest(aBrowser);
+ } catch (e) {
+ aTest.run(e);
+ }
+}
diff --git a/dom/security/test/csp/file_CSP.css b/dom/security/test/csp/file_CSP.css
new file mode 100644
index 000000000..6835c4d4a
--- /dev/null
+++ b/dom/security/test/csp/file_CSP.css
@@ -0,0 +1,20 @@
+/*
+ * Moved this CSS from an inline stylesheet to an external file when we added
+ * inline-style blocking in bug 763879.
+ * This test may hang if the load for this .css file is blocked due to a
+ * malfunction of CSP, but should pass if the style_good test passes.
+ */
+
+/* CSS font embedding tests */
+@font-face {
+ font-family: "arbitrary_good";
+ src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
+}
+@font-face {
+ font-family: "arbitrary_bad";
+ src: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
+}
+
+.div_arbitrary_good { font-family: "arbitrary_good"; }
+.div_arbitrary_bad { font-family: "arbitrary_bad"; }
+
diff --git a/dom/security/test/csp/file_CSP.sjs b/dom/security/test/csp/file_CSP.sjs
new file mode 100644
index 000000000..85c2df3ba
--- /dev/null
+++ b/dom/security/test/csp/file_CSP.sjs
@@ -0,0 +1,26 @@
+// SJS file for CSP mochitests
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var isPreflight = request.method == "OPTIONS";
+
+
+ //avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if ("type" in query) {
+ response.setHeader("Content-Type", unescape(query['type']), false);
+ } else {
+ response.setHeader("Content-Type", "text/html", false);
+ }
+
+ if ("content" in query) {
+ response.write(unescape(query['content']));
+ }
+}
diff --git a/dom/security/test/csp/file_allow_https_schemes.html b/dom/security/test/csp/file_allow_https_schemes.html
new file mode 100644
index 000000000..787e683e8
--- /dev/null
+++ b/dom/security/test/csp/file_allow_https_schemes.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 826805 - CSP: Allow http and https for scheme-less sources</title>
+ </head>
+ <body>
+ <div id="testdiv">blocked</div>
+ <!--
+ We resue file_path_matching.js which just updates the contents of 'testdiv' to contain allowed.
+ Note, that we are loading the file_path_matchting.js using a scheme of 'https'.
+ -->
+ <script src="https://example.com/tests/dom/security/test/csp/file_path_matching.js#foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_base_uri_server.sjs b/dom/security/test/csp/file_base_uri_server.sjs
new file mode 100644
index 000000000..dfba2a061
--- /dev/null
+++ b/dom/security/test/csp/file_base_uri_server.sjs
@@ -0,0 +1,61 @@
+// Custom *.sjs file specifically for the needs of
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1263286
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const PRE_BASE = `
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>Bug 1045897 - Test CSP base-uri directive</title>`;
+
+const REGULAR_POST_BASE =`
+ </head>
+ <body onload='window.parent.postMessage({result: document.baseURI}, "*");'>
+ <!-- just making use of the 'base' tag for this test -->
+ </body>
+ </html>`;
+
+const SCRIPT_POST_BASE = `
+ </head>
+ <body>
+ <script>
+ document.getElementById("base1").removeAttribute("href");
+ window.parent.postMessage({result: document.baseURI}, "*");
+ </script>
+ </body>
+ </html>`;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // Deliver the CSP policy encoded in the URL
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+
+ // Send HTML to test allowed/blocked behaviors
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(PRE_BASE);
+ var base1 =
+ "<base id=\"base1\" href=\"" + query.get("base1") + "\">";
+ var base2 =
+ "<base id=\"base2\" href=\"" + query.get("base2") + "\">";
+ response.write(base1 + base2);
+
+ if (query.get("action") === "enforce-csp") {
+ response.write(REGULAR_POST_BASE);
+ return;
+ }
+
+ if (query.get("action") === "remove-base1") {
+ response.write(SCRIPT_POST_BASE);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_blob_data_schemes.html b/dom/security/test/csp/file_blob_data_schemes.html
new file mode 100644
index 000000000..0a4a49160
--- /dev/null
+++ b/dom/security/test/csp/file_blob_data_schemes.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1086999 - Wildcard should not match blob:, data:</title>
+</head>
+<body>
+<script type="text/javascript">
+
+var base64data =
+"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+"P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+
+
+// construct an image element using *data:*
+var data_src = "data:image/png;base64," + base64data;
+var data_img = document.createElement('img');
+data_img.onload = function() {
+ window.parent.postMessage({scheme: "data", result: "allowed"}, "*");
+}
+data_img.onerror = function() {
+ window.parent.postMessage({scheme: "data", result: "blocked"}, "*");
+}
+data_img.src = data_src;
+document.body.appendChild(data_img);
+
+
+// construct an image element using *blob:*
+var byteCharacters = atob(base64data);
+var byteNumbers = new Array(byteCharacters.length);
+for (var i = 0; i < byteCharacters.length; i++) {
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
+}
+var byteArray = new Uint8Array(byteNumbers);
+var blob = new Blob([byteArray], {type: "image/png"});
+var imageUrl = URL.createObjectURL( blob );
+
+var blob_img = document.createElement('img');
+blob_img.onload = function() {
+ window.parent.postMessage({scheme: "blob", result: "allowed"}, "*");
+}
+blob_img.onerror = function() {
+ window.parent.postMessage({scheme: "blob", result: "blocked"}, "*");
+}
+blob_img.src = imageUrl;
+document.body.appendChild(blob_img);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_block_all_mcb.sjs b/dom/security/test/csp/file_block_all_mcb.sjs
new file mode 100644
index 000000000..731553dd7
--- /dev/null
+++ b/dom/security/test/csp/file_block_all_mcb.sjs
@@ -0,0 +1,76 @@
+// custom *.sjs for Bug 1122236
+// CSP: 'block-all-mixed-content'
+
+const HEAD =
+ "<!DOCTYPE HTML>" +
+ "<html><head><meta charset=\"utf-8\">" +
+ "<title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>" +
+ "</head>";
+
+const CSP_ALLOW =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"img-src *\">";
+
+const CSP_BLOCK =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"block-all-mixed-content\">";
+
+const BODY =
+ "<body>" +
+ "<img id=\"testimage\" src=\"http://mochi.test:8888/tests/image/test/mochitest/blue.png\"></img>" +
+ "<script type=\"application/javascript\">" +
+ " var myImg = document.getElementById(\"testimage\");" +
+ " myImg.onload = function(e) {" +
+ " window.parent.postMessage({result: \"img-loaded\"}, \"*\");" +
+ " };" +
+ " myImg.onerror = function(e) {" +
+ " window.parent.postMessage({result: \"img-blocked\"}, \"*\");" +
+ " };" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+// We have to use this special code fragment, in particular '?nocache' to trigger an
+// actual network load rather than loading the image from the cache.
+const BODY_CSPRO =
+ "<body>" +
+ "<img id=\"testimage\" src=\"http://mochi.test:8888/tests/image/test/mochitest/blue.png?nocache\"></img>" +
+ "<script type=\"application/javascript\">" +
+ " var myImg = document.getElementById(\"testimage\");" +
+ " myImg.onload = function(e) {" +
+ " window.parent.postMessage({result: \"img-loaded\"}, \"*\");" +
+ " };" +
+ " myImg.onerror = function(e) {" +
+ " window.parent.postMessage({result: \"img-blocked\"}, \"*\");" +
+ " };" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var queryString = request.queryString;
+
+ if (queryString === "csp-block") {
+ response.write(HEAD + CSP_BLOCK + BODY);
+ return;
+ }
+ if (queryString === "csp-allow") {
+ response.write(HEAD + CSP_ALLOW + BODY);
+ return;
+ }
+ if (queryString === "no-csp") {
+ response.write(HEAD + BODY);
+ return;
+ }
+ if (queryString === "cspro-block") {
+ // CSP RO is not supported in meta tag, let's use the header
+ response.setHeader("Content-Security-Policy-Report-Only", "block-all-mixed-content", false);
+ response.write(HEAD + BODY_CSPRO);
+ return;
+ }
+ // we should never get here but just in case return something unexpected
+ response.write("do'h");
+
+}
diff --git a/dom/security/test/csp/file_block_all_mixed_content_frame_navigation1.html b/dom/security/test/csp/file_block_all_mixed_content_frame_navigation1.html
new file mode 100644
index 000000000..fdc1ae87a
--- /dev/null
+++ b/dom/security/test/csp/file_block_all_mixed_content_frame_navigation1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content="block-all-mixed-content">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+</head>
+<body>
+<b>user clicks and navigates from https://b.com to http://c.com</b>
+
+<a id="navlink" href="http://example.com/tests/dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html">foo</a>
+
+<script class="testbody" type="text/javascript">
+ // click the link to start the frame navigation
+ document.getElementById("navlink").click();
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html b/dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html
new file mode 100644
index 000000000..4c4084e9e
--- /dev/null
+++ b/dom/security/test/csp/file_block_all_mixed_content_frame_navigation2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+</head>
+<body>
+<b>http://c.com loaded, let's tell the parent</b>
+
+<script class="testbody" type="text/javascript">
+ window.parent.postMessage({result: "frame-navigated"}, "*");
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug1229639.html b/dom/security/test/csp/file_bug1229639.html
new file mode 100644
index 000000000..1e6152ead
--- /dev/null
+++ b/dom/security/test/csp/file_bug1229639.html
@@ -0,0 +1,7 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- this should be allowed -->
+ <script src="http://mochi.test:8888/tests/dom/security/test/csp/%24.js"> </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug1229639.html^headers^ b/dom/security/test/csp/file_bug1229639.html^headers^
new file mode 100644
index 000000000..0177de7a3
--- /dev/null
+++ b/dom/security/test/csp/file_bug1229639.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: "default-src 'self'; script-src http://mochi.test:8888/tests/dom/security/test/csp/%24.js \ No newline at end of file
diff --git a/dom/security/test/csp/file_bug1312272.html b/dom/security/test/csp/file_bug1312272.html
new file mode 100644
index 000000000..18e0e5589
--- /dev/null
+++ b/dom/security/test/csp/file_bug1312272.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>marquee inline script tests for Bug 1312272</title>
+</head>
+<body>
+<marquee id="m" onstart="parent.postMessage('csp-violation-marquee-onstart', '*')">bug 1312272</marquee>
+<script src="file_bug1312272.js"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug1312272.html^headers^ b/dom/security/test/csp/file_bug1312272.html^headers^
new file mode 100644
index 000000000..25a9483ea
--- /dev/null
+++ b/dom/security/test/csp/file_bug1312272.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src *; script-src * 'unsafe-eval'
diff --git a/dom/security/test/csp/file_bug1312272.js b/dom/security/test/csp/file_bug1312272.js
new file mode 100644
index 000000000..01c03e43f
--- /dev/null
+++ b/dom/security/test/csp/file_bug1312272.js
@@ -0,0 +1,8 @@
+var m = document.getElementById("m");
+m.addEventListener("click", function() {
+ // this will trigger after onstart, obviously.
+ parent.postMessage('finish', '*');
+});
+console.log("finish-handler setup");
+m.click();
+console.log("clicked");
diff --git a/dom/security/test/csp/file_bug663567.xsl b/dom/security/test/csp/file_bug663567.xsl
new file mode 100644
index 000000000..b12b0d3b1
--- /dev/null
+++ b/dom/security/test/csp/file_bug663567.xsl
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Edited by XMLSpy® -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/">
+ <html>
+ <body>
+ <h2 id="xsltheader">this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!</h2>
+ <table border="1">
+ <tr bgcolor="#990099">
+ <th>Title</th>
+ <th>Artist</th>
+ <th>Price</th>
+ </tr>
+ <xsl:for-each select="catalog/cd">
+ <tr>
+ <td><xsl:value-of select="title"/></td>
+ <td><xsl:value-of select="artist"/></td>
+ <td><xsl:value-of select="price"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+</xsl:stylesheet>
+
diff --git a/dom/security/test/csp/file_bug663567_allows.xml b/dom/security/test/csp/file_bug663567_allows.xml
new file mode 100644
index 000000000..93d345103
--- /dev/null
+++ b/dom/security/test/csp/file_bug663567_allows.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="file_bug663567.xsl"?>
+<catalog>
+ <cd>
+ <title>Empire Burlesque</title>
+ <artist>Bob Dylan</artist>
+ <country>USA</country>
+ <company>Columbia</company>
+ <price>10.90</price>
+ <year>1985</year>
+ </cd>
+ <cd>
+ <title>Hide your heart</title>
+ <artist>Bonnie Tyler</artist>
+ <country>UK</country>
+ <company>CBS Records</company>
+ <price>9.90</price>
+ <year>1988</year>
+ </cd>
+ <cd>
+ <title>Greatest Hits</title>
+ <artist>Dolly Parton</artist>
+ <country>USA</country>
+ <company>RCA</company>
+ <price>9.90</price>
+ <year>1982</year>
+ </cd>
+</catalog>
diff --git a/dom/security/test/csp/file_bug663567_allows.xml^headers^ b/dom/security/test/csp/file_bug663567_allows.xml^headers^
new file mode 100644
index 000000000..4c6fa3c26
--- /dev/null
+++ b/dom/security/test/csp/file_bug663567_allows.xml^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'
diff --git a/dom/security/test/csp/file_bug663567_blocks.xml b/dom/security/test/csp/file_bug663567_blocks.xml
new file mode 100644
index 000000000..93d345103
--- /dev/null
+++ b/dom/security/test/csp/file_bug663567_blocks.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="file_bug663567.xsl"?>
+<catalog>
+ <cd>
+ <title>Empire Burlesque</title>
+ <artist>Bob Dylan</artist>
+ <country>USA</country>
+ <company>Columbia</company>
+ <price>10.90</price>
+ <year>1985</year>
+ </cd>
+ <cd>
+ <title>Hide your heart</title>
+ <artist>Bonnie Tyler</artist>
+ <country>UK</country>
+ <company>CBS Records</company>
+ <price>9.90</price>
+ <year>1988</year>
+ </cd>
+ <cd>
+ <title>Greatest Hits</title>
+ <artist>Dolly Parton</artist>
+ <country>USA</country>
+ <company>RCA</company>
+ <price>9.90</price>
+ <year>1982</year>
+ </cd>
+</catalog>
diff --git a/dom/security/test/csp/file_bug663567_blocks.xml^headers^ b/dom/security/test/csp/file_bug663567_blocks.xml^headers^
new file mode 100644
index 000000000..baf7f3c6a
--- /dev/null
+++ b/dom/security/test/csp/file_bug663567_blocks.xml^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src *.example.com
diff --git a/dom/security/test/csp/file_bug802872.html b/dom/security/test/csp/file_bug802872.html
new file mode 100644
index 000000000..dc7129b0c
--- /dev/null
+++ b/dom/security/test/csp/file_bug802872.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 802872</title>
+ <!-- Including SimpleTest.js so we can use AddLoadEvent !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <script src='file_bug802872.js'></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug802872.html^headers^ b/dom/security/test/csp/file_bug802872.html^headers^
new file mode 100644
index 000000000..4c6fa3c26
--- /dev/null
+++ b/dom/security/test/csp/file_bug802872.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'
diff --git a/dom/security/test/csp/file_bug802872.js b/dom/security/test/csp/file_bug802872.js
new file mode 100644
index 000000000..5df8086cc
--- /dev/null
+++ b/dom/security/test/csp/file_bug802872.js
@@ -0,0 +1,43 @@
+/*
+ * The policy for this test is:
+ * Content-Security-Policy: default-src 'self'
+ */
+
+function createAllowedEvent() {
+ /*
+ * Creates a new EventSource using 'http://mochi.test:8888'. Since all mochitests run on
+ * 'http://mochi.test', a default-src of 'self' allows this request.
+ */
+ var src_event = new EventSource("http://mochi.test:8888/tests/dom/security/test/csp/file_bug802872.sjs");
+
+ src_event.onmessage = function(e) {
+ src_event.close();
+ parent.dispatchEvent(new Event('allowedEventSrcCallbackOK'));
+ }
+
+ src_event.onerror = function(e) {
+ src_event.close();
+ parent.dispatchEvent(new Event('allowedEventSrcCallbackFailed'));
+ }
+}
+
+function createBlockedEvent() {
+ /*
+ * creates a new EventSource using 'http://example.com'. This domain is not whitelisted by the
+ * CSP of this page, therefore the CSP blocks this request.
+ */
+ var src_event = new EventSource("http://example.com/tests/dom/security/test/csp/file_bug802872.sjs");
+
+ src_event.onmessage = function(e) {
+ src_event.close();
+ parent.dispatchEvent(new Event('blockedEventSrcCallbackOK'));
+ }
+
+ src_event.onerror = function(e) {
+ src_event.close();
+ parent.dispatchEvent(new Event('blockedEventSrcCallbackFailed'));
+ }
+}
+
+addLoadEvent(createAllowedEvent);
+addLoadEvent(createBlockedEvent);
diff --git a/dom/security/test/csp/file_bug802872.sjs b/dom/security/test/csp/file_bug802872.sjs
new file mode 100644
index 000000000..b3e3f7024
--- /dev/null
+++ b/dom/security/test/csp/file_bug802872.sjs
@@ -0,0 +1,7 @@
+function handleRequest(request, response)
+{
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/event-stream", false);
+ response.write("data: eventsource response from server!");
+ response.write("\n\n");
+}
diff --git a/dom/security/test/csp/file_bug836922_npolicies.html b/dom/security/test/csp/file_bug836922_npolicies.html
new file mode 100644
index 000000000..6a728813a
--- /dev/null
+++ b/dom/security/test/csp/file_bug836922_npolicies.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <link rel='stylesheet' type='text/css'
+ href='/tests/dom/security/test/csp/file_CSP.sjs?testid=css_self&type=text/css' />
+
+ </head>
+ <body>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img_self&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script_self&type=text/javascript'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug836922_npolicies.html^headers^ b/dom/security/test/csp/file_bug836922_npolicies.html^headers^
new file mode 100644
index 000000000..ec6ba8c4a
--- /dev/null
+++ b/dom/security/test/csp/file_bug836922_npolicies.html^headers^
@@ -0,0 +1,2 @@
+content-security-policy: default-src 'self'; img-src 'none'; report-uri http://mochi.test:8888/tests/dom/security/test/csp/file_bug836922_npolicies_violation.sjs
+content-security-policy-report-only: default-src *; img-src 'self'; script-src 'none'; report-uri http://mochi.test:8888/tests/dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs
diff --git a/dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs b/dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs
new file mode 100644
index 000000000..3e7603421
--- /dev/null
+++ b/dom/security/test/csp/file_bug836922_npolicies_ro_violation.sjs
@@ -0,0 +1,53 @@
+// SJS file that receives violation reports and then responds with nothing.
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+const STATE_KEY = "bug836922_ro_violations";
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ if ('results' in query) {
+ // if asked for the received data, send it.
+ response.setHeader("Content-Type", "text/javascript", false);
+ if (getState(STATE_KEY)) {
+ response.write(getState(STATE_KEY));
+ } else {
+ // no state has been recorded.
+ response.write(JSON.stringify({}));
+ }
+ } else if ('reset' in query) {
+ //clear state
+ setState(STATE_KEY, JSON.stringify(null));
+ } else {
+ // ... otherwise, just respond "ok".
+ response.write("null");
+
+ var bodystream = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+ while ((avail = bodystream.available()) > 0)
+ Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
+
+ var data = String.fromCharCode.apply(null, bytes);
+
+ // figure out which test was violating a policy
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+ var testid = testpat.exec(data)[1];
+
+ // store the violation in the persistent state
+ var s = JSON.parse(getState(STATE_KEY) || "{}");
+ s[testid] ? s[testid]++ : s[testid] = 1;
+ setState(STATE_KEY, JSON.stringify(s));
+ }
+}
+
+
diff --git a/dom/security/test/csp/file_bug836922_npolicies_violation.sjs b/dom/security/test/csp/file_bug836922_npolicies_violation.sjs
new file mode 100644
index 000000000..15e4958af
--- /dev/null
+++ b/dom/security/test/csp/file_bug836922_npolicies_violation.sjs
@@ -0,0 +1,59 @@
+// SJS file that receives violation reports and then responds with nothing.
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+const STATE = "bug836922_violations";
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+
+ if ('results' in query) {
+ // if asked for the received data, send it.
+ response.setHeader("Content-Type", "text/javascript", false);
+ if (getState(STATE)) {
+ response.write(getState(STATE));
+ } else {
+ // no state has been recorded.
+ response.write(JSON.stringify({}));
+ }
+ } else if ('reset' in query) {
+ //clear state
+ setState(STATE, JSON.stringify(null));
+ } else {
+ // ... otherwise, just respond "ok".
+ response.write("null");
+
+ var bodystream = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+ while ((avail = bodystream.available()) > 0)
+ Array.prototype.push.apply(bytes, bodystream.readByteArray(avail));
+
+ var data = String.fromCharCode.apply(null, bytes);
+
+ // figure out which test was violating a policy
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+ var testid = testpat.exec(data)[1];
+
+ // store the violation in the persistent state
+ var s = getState(STATE);
+ if (!s) s = "{}";
+ s = JSON.parse(s);
+ if (!s) s = {};
+
+ if (!s[testid]) s[testid] = 0;
+ s[testid]++;
+ setState(STATE, JSON.stringify(s));
+ }
+}
+
+
diff --git a/dom/security/test/csp/file_bug885433_allows.html b/dom/security/test/csp/file_bug885433_allows.html
new file mode 100644
index 000000000..5d7aacbda
--- /dev/null
+++ b/dom/security/test/csp/file_bug885433_allows.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<!--
+The Content-Security-Policy header for this file is:
+
+ Content-Security-Policy: img-src 'self';
+
+It does not include any of the default-src, script-src, or style-src
+directives. It should allow the use of unsafe-inline and unsafe-eval on
+scripts, and unsafe-inline on styles, because no directives related to scripts
+or styles are specified.
+-->
+<html>
+<body>
+ <ol>
+ <li id="unsafe-inline-script-allowed">Inline script allowed (this text should be green)</li>
+ <li id="unsafe-eval-script-allowed">Eval script allowed (this text should be green)</li>
+ <li id="unsafe-inline-style-allowed">Inline style allowed (this text should be green)</li>
+ </ol>
+
+ <script>
+ // Use inline script to set a style attribute
+ document.getElementById("unsafe-inline-script-allowed").style.color = "green";
+
+ // Use eval to set a style attribute
+ // try/catch is used because CSP causes eval to throw an exception when it
+ // is blocked, which would derail the rest of the tests in this file.
+ try {
+ eval('document.getElementById("unsafe-eval-script-allowed").style.color = "green";');
+ } catch (e) {}
+ </script>
+
+ <style>
+ li#unsafe-inline-style-allowed {
+ color: green;
+ }
+ </style>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug885433_allows.html^headers^ b/dom/security/test/csp/file_bug885433_allows.html^headers^
new file mode 100644
index 000000000..767b9ca92
--- /dev/null
+++ b/dom/security/test/csp/file_bug885433_allows.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: img-src 'self';
diff --git a/dom/security/test/csp/file_bug885433_blocks.html b/dom/security/test/csp/file_bug885433_blocks.html
new file mode 100644
index 000000000..2279b33e4
--- /dev/null
+++ b/dom/security/test/csp/file_bug885433_blocks.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<!--
+The Content-Security-Policy header for this file is:
+
+ Content-Security-Policy: default-src 'self';
+
+The Content-Security-Policy header for this file includes the default-src
+directive, which triggers the default behavior of blocking unsafe-inline and
+unsafe-eval on scripts, and unsafe-inline on styles.
+-->
+<html>
+<body>
+ <ol>
+ <li id="unsafe-inline-script-blocked">Inline script blocked (this text should be black)</li>
+ <li id="unsafe-eval-script-blocked">Eval script blocked (this text should be black)</li>
+ <li id="unsafe-inline-style-blocked">Inline style blocked (this text should be black)</li>
+ </ol>
+
+ <script>
+ // Use inline script to set a style attribute
+ document.getElementById("unsafe-inline-script-blocked").style.color = "green";
+
+ // Use eval to set a style attribute
+ // try/catch is used because CSP causes eval to throw an exception when it
+ // is blocked, which would derail the rest of the tests in this file.
+ try {
+ eval('document.getElementById("unsafe-eval-script-blocked").style.color = "green";');
+ } catch (e) {}
+ </script>
+
+ <style>
+ li#unsafe-inline-style-blocked {
+ color: green;
+ }
+ </style>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug885433_blocks.html^headers^ b/dom/security/test/csp/file_bug885433_blocks.html^headers^
new file mode 100644
index 000000000..f82598b67
--- /dev/null
+++ b/dom/security/test/csp/file_bug885433_blocks.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self';
diff --git a/dom/security/test/csp/file_bug886164.html b/dom/security/test/csp/file_bug886164.html
new file mode 100644
index 000000000..ec8c9e7e9
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164.html
@@ -0,0 +1,15 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox="allow-same-origin" -->
+ <!-- Content-Security-Policy: default-src 'self' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164.html^headers^ b/dom/security/test/csp/file_bug886164.html^headers^
new file mode 100644
index 000000000..4c6fa3c26
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'
diff --git a/dom/security/test/csp/file_bug886164_2.html b/dom/security/test/csp/file_bug886164_2.html
new file mode 100644
index 000000000..83d36c55a
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_2.html
@@ -0,0 +1,14 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox -->
+ <!-- Content-Security-Policy: default-src 'self' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164_2.html^headers^ b/dom/security/test/csp/file_bug886164_2.html^headers^
new file mode 100644
index 000000000..4c6fa3c26
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_2.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'
diff --git a/dom/security/test/csp/file_bug886164_3.html b/dom/security/test/csp/file_bug886164_3.html
new file mode 100644
index 000000000..8b4313000
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_3.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox -->
+ <!-- Content-Security-Policy: default-src 'none' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164_3.html^headers^ b/dom/security/test/csp/file_bug886164_3.html^headers^
new file mode 100644
index 000000000..6581fd425
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_3.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'none'
diff --git a/dom/security/test/csp/file_bug886164_4.html b/dom/security/test/csp/file_bug886164_4.html
new file mode 100644
index 000000000..41137ea01
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_4.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox -->
+ <!-- Content-Security-Policy: default-src 'none' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164_4.html^headers^ b/dom/security/test/csp/file_bug886164_4.html^headers^
new file mode 100644
index 000000000..6581fd425
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_4.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'none'
diff --git a/dom/security/test/csp/file_bug886164_5.html b/dom/security/test/csp/file_bug886164_5.html
new file mode 100644
index 000000000..ae65171a5
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_5.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+ }
+</script>
+<script src='file_iframe_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with only inline "allow-scripts"
+
+ <!-- sandbox="allow-scripts" -->
+ <!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164_5.html^headers^ b/dom/security/test/csp/file_bug886164_5.html^headers^
new file mode 100644
index 000000000..3abc19055
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_5.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'none' 'unsafe-inline';
diff --git a/dom/security/test/csp/file_bug886164_6.html b/dom/security/test/csp/file_bug886164_6.html
new file mode 100644
index 000000000..f985ec8ce
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+
+ document.getElementById('a_form').submit();
+
+ // trigger the javascript: url test
+ sendMouseEvent({type:'click'}, 'a_link');
+ }
+</script>
+<script src='file_iframe_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with "allow-scripts"
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
+
+ <form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" onclick="doSubmit()" id="a_button">
+ </form>
+
+ <a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_bug886164_6.html^headers^ b/dom/security/test/csp/file_bug886164_6.html^headers^
new file mode 100644
index 000000000..6f9fc3f25
--- /dev/null
+++ b/dom/security/test/csp/file_bug886164_6.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self' 'unsafe-inline';
diff --git a/dom/security/test/csp/file_bug888172.html b/dom/security/test/csp/file_bug888172.html
new file mode 100644
index 000000000..27cf9b00a
--- /dev/null
+++ b/dom/security/test/csp/file_bug888172.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <body>
+ <ol>
+ <li id="unsafe-inline-script">Inline script (green if allowed, black if blocked)</li>
+ <li id="unsafe-eval-script">Eval script (green if allowed, black if blocked)</li>
+ <li id="unsafe-inline-style">Inline style (green if allowed, black if blocked)</li>
+ </ol>
+
+ <script>
+ // Use inline script to set a style attribute
+ document.getElementById("unsafe-inline-script").style.color = "green";
+
+ // Use eval to set a style attribute
+ // try/catch is used because CSP causes eval to throw an exception when it
+ // is blocked, which would derail the rest of the tests in this file.
+ try {
+ eval('document.getElementById("unsafe-eval-script").style.color = "green";');
+ } catch (e) {}
+ </script>
+
+ <style>
+ li#unsafe-inline-style {
+ color: green;
+ }
+ </style>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug888172.sjs b/dom/security/test/csp/file_bug888172.sjs
new file mode 100644
index 000000000..03309610f
--- /dev/null
+++ b/dom/security/test/csp/file_bug888172.sjs
@@ -0,0 +1,43 @@
+// SJS file for CSP mochitests
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function loadHTMLFromFile(path) {
+ // Load the HTML to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ var testHTMLFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testHTMLFile.append(dirs[i]);
+ }
+ var testHTMLFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+ var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
+ return testHTML;
+}
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // Deliver the CSP policy encoded in the URI
+ if (query['csp'])
+ response.setHeader("Content-Security-Policy", unescape(query['csp']), false);
+
+ // Send HTML to test allowed/blocked behaviors
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(loadHTMLFromFile("tests/dom/security/test/csp/file_bug888172.html"));
+}
diff --git a/dom/security/test/csp/file_bug909029_none.html b/dom/security/test/csp/file_bug909029_none.html
new file mode 100644
index 000000000..0d4934a4a
--- /dev/null
+++ b/dom/security/test/csp/file_bug909029_none.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+ <head>
+ <!-- file_CSP.sjs mocks a resource load -->
+ <link rel='stylesheet' type='text/css'
+ href='file_CSP.sjs?testid=noneExternalStylesBlocked&type=text/css' />
+ </head>
+ <body>
+ <p id="inline-style">This should be green</p>
+ <p id="inline-script">This should be black</p>
+ <style>
+ p#inline-style { color:rgb(0, 128, 0); }
+ </style>
+ <script>
+ // Use inline script to set a style attribute
+ document.getElementById("inline-script").style.color = "rgb(0, 128, 0)";
+ </script>
+ <img src="file_CSP.sjs?testid=noneExternalImgLoaded&type=img/png" />
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug909029_none.html^headers^ b/dom/security/test/csp/file_bug909029_none.html^headers^
new file mode 100644
index 000000000..ecb345875
--- /dev/null
+++ b/dom/security/test/csp/file_bug909029_none.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src * ; style-src 'none' 'unsafe-inline';
diff --git a/dom/security/test/csp/file_bug909029_star.html b/dom/security/test/csp/file_bug909029_star.html
new file mode 100644
index 000000000..bcb907a96
--- /dev/null
+++ b/dom/security/test/csp/file_bug909029_star.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head>
+ <link rel='stylesheet' type='text/css'
+ href='file_CSP.sjs?testid=starExternalStylesLoaded&type=text/css' />
+ </head>
+ <body>
+ <p id="inline-style">This should be green</p>
+ <p id="inline-script">This should be black</p>
+ <style>
+ p#inline-style { color:rgb(0, 128, 0); }
+ </style>
+ <script>
+ // Use inline script to set a style attribute
+ document.getElementById("inline-script").style.color = "rgb(0, 128, 0)";
+ </script>
+ <img src="file_CSP.sjs?testid=starExternalImgLoaded&type=img/png" />
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug909029_star.html^headers^ b/dom/security/test/csp/file_bug909029_star.html^headers^
new file mode 100644
index 000000000..eccc1c011
--- /dev/null
+++ b/dom/security/test/csp/file_bug909029_star.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src *; style-src * 'unsafe-inline';
diff --git a/dom/security/test/csp/file_bug910139.sjs b/dom/security/test/csp/file_bug910139.sjs
new file mode 100644
index 000000000..172cc09c9
--- /dev/null
+++ b/dom/security/test/csp/file_bug910139.sjs
@@ -0,0 +1,52 @@
+// Server side js file for bug 910139, see file test_bug910139.html for details.
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function loadResponseFromFile(path) {
+ var testHTMLFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testHTMLFile.append(dirs[i]);
+ }
+ var testHTMLFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+ var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
+ return testHTML;
+}
+
+var policies = [
+ "default-src 'self'; script-src 'self'", // CSP for checkAllowed
+ "default-src 'self'; script-src *.example.com" // CSP for checkBlocked
+]
+
+function getPolicy() {
+ var index;
+ // setState only accepts strings as arguments
+ if (!getState("counter")) {
+ index = 0;
+ setState("counter", index.toString());
+ }
+ else {
+ index = parseInt(getState("counter"));
+ ++index;
+ setState("counter", index.toString());
+ }
+ return policies[index];
+}
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // set the required CSP
+ response.setHeader("Content-Security-Policy", getPolicy(), false);
+
+ // return the requested XML file.
+ response.write(loadResponseFromFile("tests/dom/security/test/csp/file_bug910139.xml"));
+}
diff --git a/dom/security/test/csp/file_bug910139.xml b/dom/security/test/csp/file_bug910139.xml
new file mode 100644
index 000000000..29feba941
--- /dev/null
+++ b/dom/security/test/csp/file_bug910139.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<?xml-stylesheet type="text/xsl" href="file_bug910139.xsl"?>
+<catalog>
+ <cd>
+ <title>Empire Burlesque</title>
+ <artist>Bob Dylan</artist>
+ <country>USA</country>
+ <company>Columbia</company>
+ <price>10.90</price>
+ <year>1985</year>
+ </cd>
+ <cd>
+ <title>Hide your heart</title>
+ <artist>Bonnie Tyler</artist>
+ <country>UK</country>
+ <company>CBS Records</company>
+ <price>9.90</price>
+ <year>1988</year>
+ </cd>
+ <cd>
+ <title>Greatest Hits</title>
+ <artist>Dolly Parton</artist>
+ <country>USA</country>
+ <company>RCA</company>
+ <price>9.90</price>
+ <year>1982</year>
+ </cd>
+</catalog>
diff --git a/dom/security/test/csp/file_bug910139.xsl b/dom/security/test/csp/file_bug910139.xsl
new file mode 100644
index 000000000..b99abca09
--- /dev/null
+++ b/dom/security/test/csp/file_bug910139.xsl
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!-- Edited by XMLSpy® -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/">
+ <html>
+ <body>
+ <h2 id="xsltheader">this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!</h2>
+ <table border="1">
+ <tr bgcolor="#990099">
+ <th>Title</th>
+ <th>Artist</th>
+ <th>Price</th>
+ </tr>
+ <xsl:for-each select="catalog/cd">
+ <tr>
+ <td><xsl:value-of select="title"/></td>
+ <td><xsl:value-of select="artist"/></td>
+ <td><xsl:value-of select="price"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+</xsl:template>
+</xsl:stylesheet>
+
diff --git a/dom/security/test/csp/file_bug941404.html b/dom/security/test/csp/file_bug941404.html
new file mode 100644
index 000000000..3a2e636e0
--- /dev/null
+++ b/dom/security/test/csp/file_bug941404.html
@@ -0,0 +1,27 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+
+ <!-- this should be allowed (no CSP)-->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_good&type=img/png"> </img>
+
+
+ <script type="text/javascript">
+ var req = new XMLHttpRequest();
+ req.onload = function() {
+ //this should be allowed (no CSP)
+ try {
+ var img = document.createElement("img");
+ img.src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_good&type=img/png";
+ document.body.appendChild(img);
+ } catch(e) {
+ console.log("yo: "+e);
+ }
+ };
+ req.open("get", "file_bug941404_xhr.html", true);
+ req.responseType = "document";
+ req.send();
+ </script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug941404_xhr.html b/dom/security/test/csp/file_bug941404_xhr.html
new file mode 100644
index 000000000..22e176f20
--- /dev/null
+++ b/dom/security/test/csp/file_bug941404_xhr.html
@@ -0,0 +1,5 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_bug941404_xhr.html^headers^ b/dom/security/test/csp/file_bug941404_xhr.html^headers^
new file mode 100644
index 000000000..1e5f70cc3
--- /dev/null
+++ b/dom/security/test/csp/file_bug941404_xhr.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'none' 'unsafe-inline' 'unsafe-eval'
diff --git a/dom/security/test/csp/file_child-src_iframe.html b/dom/security/test/csp/file_child-src_iframe.html
new file mode 100644
index 000000000..3534aa329
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_iframe.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <iframe id="testframe"> </iframe>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+
+ function executeTest(ev) {
+ testframe = document.getElementById('testframe');
+ testframe.contentWindow.postMessage({id:page_id, message:"execute"}, 'http://mochi.test:8888');
+ }
+
+ function reportError(ev) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ cleanup();
+ }
+
+ function recvMessage(ev) {
+ if (ev.data.id == page_id) {
+ window.parent.postMessage({id:ev.data.id, message:ev.data.message}, 'http://mochi.test:8888');
+ cleanup();
+ }
+ }
+
+ function cleanup() {
+ testframe = document.getElementById('testframe');
+ window.removeEventListener('message', recvMessage);
+ testframe.removeEventListener('load', executeTest);
+ testframe.removeEventListener('error', reportError);
+ }
+
+
+ window.addEventListener('message', recvMessage, false);
+
+ try {
+ // Please note that file_testserver.sjs?foo does not return a response.
+ // For testing purposes this is not necessary because we only want to check
+ // whether CSP allows or blocks the load.
+ src = "file_testserver.sjs";
+ src += "?file=" + escape("tests/dom/security/test/csp/file_child-src_inner_frame.html");
+ src += "#" + escape(page_id);
+ testframe = document.getElementById('testframe');
+
+ testframe.addEventListener('load', executeTest, false);
+ testframe.addEventListener('error', reportError, false);
+
+ testframe.src = src;
+ }
+ catch (e) {
+ if (e.message.match(/Failed to load script/)) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ } else {
+ window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_inner_frame.html b/dom/security/test/csp/file_child-src_inner_frame.html
new file mode 100644
index 000000000..e42102430
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_inner_frame.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <iframe id="innermosttestframe"> </iframe>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+
+ function recvMessage(ev) {
+ if (ev.data.id == page_id) {
+ window.parent.postMessage({id:ev.data.id, message:'allowed'}, 'http://mochi.test:8888');
+ window.removeEventListener('message', recvMessage);
+ }
+ }
+
+ window.addEventListener('message', recvMessage, false);
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_service_worker.html b/dom/security/test/csp/file_child-src_service_worker.html
new file mode 100644
index 000000000..b291a4a4e
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_service_worker.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+ try {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.register(
+ 'file_child-src_service_worker.js',
+ { scope: './' + page_id + '/' }
+ ).then(function(reg)
+ {
+ // registration worked
+ reg.unregister().then(function() {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+ });
+ }).catch(function(error) {
+ // registration failed
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ });
+ };
+ } catch(ex) {
+ window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_service_worker.js b/dom/security/test/csp/file_child-src_service_worker.js
new file mode 100644
index 000000000..53f768707
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_service_worker.js
@@ -0,0 +1,3 @@
+this.addEventListener('install', function(event) {
+ close();
+});
diff --git a/dom/security/test/csp/file_child-src_shared_worker-redirect.html b/dom/security/test/csp/file_child-src_shared_worker-redirect.html
new file mode 100644
index 000000000..313915302
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_shared_worker-redirect.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+ var redir = 'none';
+
+ page_id.split('_').forEach(function (val) {
+ var [name, value] = val.split('-');
+ if (name == 'redir') {
+ redir = unescape(value);
+ }
+ });
+
+ try {
+ worker = new SharedWorker('file_redirect_worker.sjs?path='
+ + escape("/tests/dom/security/test/csp/file_child-src_shared_worker.js")
+ + "&redir=" + redir
+ + "&page_id=" + page_id,
+ page_id);
+ worker.port.start();
+
+ worker.onerror = function(evt) {
+ evt.preventDefault();
+ window.parent.postMessage({id:page_id, message:"blocked"},
+ 'http://mochi.test:8888');
+ }
+
+ worker.port.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+ };
+
+ worker.onerror = function() {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ };
+
+ worker.port.postMessage('foo');
+ }
+ catch (e) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_shared_worker.html b/dom/security/test/csp/file_child-src_shared_worker.html
new file mode 100644
index 000000000..0e9a56a29
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_shared_worker.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+ try {
+ worker = new SharedWorker(
+ 'file_testserver.sjs?file='+
+ escape("tests/dom/security/test/csp/file_child-src_shared_worker.js"),
+ page_id);
+ worker.port.start();
+
+ worker.onerror = function(evt) {
+ evt.preventDefault();
+ window.parent.postMessage({id:page_id, message:"blocked"},
+ 'http://mochi.test:8888');
+ }
+
+ worker.port.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"},
+ 'http://mochi.test:8888');
+ };
+ worker.port.postMessage('foo');
+ }
+ catch (e) {
+ window.parent.postMessage({id:page_id, message:"blocked"},
+ 'http://mochi.test:8888');
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_shared_worker.js b/dom/security/test/csp/file_child-src_shared_worker.js
new file mode 100644
index 000000000..0fca1394c
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_shared_worker.js
@@ -0,0 +1,8 @@
+onconnect = function(e) {
+ var port = e.ports[0];
+ port.addEventListener('message', function(e) {
+ port.postMessage('success');
+ });
+
+ port.start();
+}
diff --git a/dom/security/test/csp/file_child-src_shared_worker_data.html b/dom/security/test/csp/file_child-src_shared_worker_data.html
new file mode 100644
index 000000000..a4befe4ca
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_shared_worker_data.html
@@ -0,0 +1,37 @@
+
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ var page_id = window.location.hash.substring(1);
+ var shared_worker = "onconnect = function(e) { " +
+ "var port = e.ports[0];" +
+ "port.addEventListener('message'," +
+ "function(e) { port.postMessage('success'); });" +
+ "port.start(); }";
+
+ try {
+ var worker = new SharedWorker('data:application/javascript;charset=UTF-8,'+
+ escape(shared_worker), page_id);
+ worker.port.start();
+
+ worker.onerror = function(evt) {
+ evt.preventDefault();
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ }
+
+ worker.port.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+ };
+
+ worker.port.postMessage('foo');
+ }
+ catch (e) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_worker-redirect.html b/dom/security/test/csp/file_child-src_worker-redirect.html
new file mode 100644
index 000000000..188f173b8
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_worker-redirect.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ var page_id = window.location.hash.substring(1);
+ var redir = 'none';
+
+ page_id.split('_').forEach(function (val) {
+ var [name, value] = val.split('-');
+ if (name == 'redir') {
+ redir = unescape(value);
+ }
+ });
+
+ try {
+ worker = new Worker('file_redirect_worker.sjs?path='
+ + escape("/tests/dom/security/test/csp/file_child-src_worker.js")
+ + "&redir=" + redir
+ + "&page_id=" + page_id
+ );
+
+ worker.onerror = function(error) {
+ var msg = error.message;
+ if (msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/)) {
+ // this means CSP blocked it
+ msg = "blocked";
+ }
+ window.parent.postMessage({id:page_id, message:msg}, 'http://mochi.test:8888');
+ error.preventDefault();
+ };
+
+ worker.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+
+ };
+ worker.postMessage('foo');
+ }
+ catch (e) {
+ if (e.message.match(/Failed to load script/)) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ } else {
+ window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_worker.html b/dom/security/test/csp/file_child-src_worker.html
new file mode 100644
index 000000000..9300d3f61
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_worker.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+ try {
+ worker = new Worker('file_testserver.sjs?file='+escape("tests/dom/security/test/csp/file_child-src_worker.js"));
+
+ worker.onerror = function(e) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ e.preventDefault();
+ }
+
+ worker.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+ }
+
+ worker.postMessage('foo');
+ }
+ catch (e) {
+ if (e.message.match(/Failed to load script/)) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ } else {
+ window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child-src_worker.js b/dom/security/test/csp/file_child-src_worker.js
new file mode 100644
index 000000000..1d93cac6b
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_worker.js
@@ -0,0 +1,4 @@
+onmessage = function(e) {
+ postMessage('worker');
+};
+
diff --git a/dom/security/test/csp/file_child-src_worker_data.html b/dom/security/test/csp/file_child-src_worker_data.html
new file mode 100644
index 000000000..e9e22f01d
--- /dev/null
+++ b/dom/security/test/csp/file_child-src_worker_data.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ page_id = window.location.hash.substring(1);
+ try {
+ worker = new Worker('data:application/javascript;charset=UTF-8,'+escape('onmessage = function(e) { postMessage("worker"); };'));
+
+ worker.onerror = function(e) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ e.preventDefault();
+ }
+
+ worker.onmessage = function(ev) {
+ window.parent.postMessage({id:page_id, message:"allowed"}, 'http://mochi.test:8888');
+ }
+
+ worker.postMessage('foo');
+ }
+ catch (e) {
+ if (e.message.match(/Failed to load script/)) {
+ window.parent.postMessage({id:page_id, message:"blocked"}, 'http://mochi.test:8888');
+ } else {
+ console.log(e);
+ window.parent.postMessage({id:page_id, message:"exception"}, 'http://mochi.test:8888');
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_child_worker.js b/dom/security/test/csp/file_child_worker.js
new file mode 100644
index 000000000..256234377
--- /dev/null
+++ b/dom/security/test/csp/file_child_worker.js
@@ -0,0 +1,39 @@
+function doXHR(uri) {
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", uri);
+ xhr.send();
+ } catch(ex) {}
+}
+
+var sameBase = "http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=";
+var crossBase = "http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=";
+
+onmessage = (e) => {
+ for (base of [sameBase, crossBase]) {
+ var prefix;
+ var suffix;
+ if (e.data.inherited == "parent") {
+ //Worker inherits CSP from parent worker
+ prefix = base + "worker_child_inherited_parent_";
+ suffix = base == sameBase ? "_good" : "_bad";
+ } else if (e.data.inherited == "document") {
+ //Worker inherits CSP from owner document -> parent worker -> subworker
+ prefix = base + "worker_child_inherited_document_";
+ suffix = base == sameBase ? "_good" : "_bad";
+ } else {
+ // Worker delivers CSP from HTTP header
+ prefix = base + "worker_child_";
+ suffix = base == sameBase ? "_same_bad" : "_cross_bad";
+ }
+
+ doXHR(prefix + "xhr" + suffix);
+ // Fetch is likely failed in subworker
+ // See Bug 1273070 - Failed to fetch in subworker
+ // Enable fetch test after the bug is fixed
+ // fetch(prefix + "xhr" + suffix);
+ try {
+ importScripts(prefix + "script" + suffix);
+ } catch(ex) {}
+ }
+}
diff --git a/dom/security/test/csp/file_child_worker.js^headers^ b/dom/security/test/csp/file_child_worker.js^headers^
new file mode 100644
index 000000000..6581fd425
--- /dev/null
+++ b/dom/security/test/csp/file_child_worker.js^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'none'
diff --git a/dom/security/test/csp/file_connect-src-fetch.html b/dom/security/test/csp/file_connect-src-fetch.html
new file mode 100644
index 000000000..ff9b2f740
--- /dev/null
+++ b/dom/security/test/csp/file_connect-src-fetch.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1139667 - Test mapping of fetch() to connect-src</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+
+ // Please note that file_testserver.sjs?foo does not return a response.
+ // For testing purposes this is not necessary because we only want to check
+ // whether CSP allows or blocks the load.
+ fetch( "file_testserver.sjs?foo");
+
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_connect-src.html b/dom/security/test/csp/file_connect-src.html
new file mode 100644
index 000000000..17a940a0e
--- /dev/null
+++ b/dom/security/test/csp/file_connect-src.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1031530 - Test mapping of XMLHttpRequest to connect-src</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+
+ try {
+ // Please note that file_testserver.sjs?foo does not return a response.
+ // For testing purposes this is not necessary because we only want to check
+ // whether CSP allows or blocks the load.
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "file_testserver.sjs?foo", false);
+ xhr.send(null);
+ }
+ catch (e) { }
+
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_data-uri_blocked.html b/dom/security/test/csp/file_data-uri_blocked.html
new file mode 100644
index 000000000..293e857e3
--- /dev/null
+++ b/dom/security/test/csp/file_data-uri_blocked.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1242019
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 587377</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <img width='1' height='1' title='' alt='' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAA6NGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMTEgNzkuMTU4MzI1LCAyMDE1LzA5LzEwLTAxOjEwOjIwICAgICAgICAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwveG1wOkNyZWF0b3JUb29sPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAxNi0wMi0wOFQxMzoyNjo1MSswOTowMDwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTYtMDItMDhUMTM6MjY6NTErMDk6MDA8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE2LTAyLTA4VDEzOjI2OjUxKzA5OjAwPC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcE1NOkluc3RhbmNlSUQ+eG1wLmlpZDplN2QyMjJjOS03Nzg2LTRhMjktOTlhMy0yYjRjNWI4YTA5MWY8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDo5OWE3ZTJmOC0wZWFhLTExNzktYTE4NS05M2Y1MjZhZGU2ZDg8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDoyYmY3M2M3Ny03NzczLTQzNjAtYThiZS1kY2UyZTgzMWQ2YmM8L3htcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPHhtcE1NOkhpc3Rvcnk+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jcmVhdGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6MmJmNzNjNzctNzc3My00MzYwLWE4YmUtZGNlMmU4MzFkNmJjPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE2LTAyLTA4VDEzOjI2OjUxKzA5OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6ZTdkMjIyYzktNzc4Ni00YTI5LTk5YTMtMmI0YzViOGEwOTFmPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE2LTAyLTA4VDEzOjI2OjUxKzA5OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoTWFjaW50b3NoKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgIDwvcmRmOlNlcT4KICAgICAgICAgPC94bXBNTTpIaXN0b3J5PgogICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3BuZzwvZGM6Zm9ybWF0PgogICAgICAgICA8cGhvdG9zaG9wOkNvbG9yTW9kZT4zPC9waG90b3Nob3A6Q29sb3JNb2RlPgogICAgICAgICA8cGhvdG9zaG9wOklDQ1Byb2ZpbGU+c1JHQiBJRUM2MTk2Ni0yLjE8L3Bob3Rvc2hvcDpJQ0NQcm9maWxlPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj43MjAwMDAvMTAwMDA8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj4xPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjE8L2V4aWY6UGl4ZWxZRGltZW5zaW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PoAp5ZwAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAABNJREFUeNpi+P//PwMAAAD//wMACPwC/tOldWEAAAAASUVORK5CYII='>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_data-uri_blocked.html^headers^ b/dom/security/test/csp/file_data-uri_blocked.html^headers^
new file mode 100644
index 000000000..f593253f9
--- /dev/null
+++ b/dom/security/test/csp/file_data-uri_blocked.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'; img-src 'none'
diff --git a/dom/security/test/csp/file_doccomment_meta.html b/dom/security/test/csp/file_doccomment_meta.html
new file mode 100644
index 000000000..a0f36a4bf
--- /dev/null
+++ b/dom/security/test/csp/file_doccomment_meta.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 663570 - Test doc.write(meta csp)</title>
+ <meta charset="utf-8">
+
+ <!-- Use doc.write() to *un*apply meta csp -->
+ <script type="application/javascript">
+ document.write("<!--");
+ </script>
+
+ <meta http-equiv="Content-Security-Policy" content= "style-src 'none'; script-src 'none'; img-src 'none'">
+ -->
+
+ <!-- try to load a css on a page where meta CSP is commented out -->
+ <link rel="stylesheet" type="text/css" href="file_docwrite_meta.css">
+
+ <!-- try to load a script on a page where meta CSP is commented out -->
+ <script id="testscript" src="file_docwrite_meta.js"></script>
+
+</head>
+<body>
+
+ <!-- try to load an image on a page where meta CSP is commented out -->
+ <img id="testimage" src="http://mochi.test:8888/tests/image/test/mochitest/blue.png"></img>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_docwrite_meta.css b/dom/security/test/csp/file_docwrite_meta.css
new file mode 100644
index 000000000..de725038b
--- /dev/null
+++ b/dom/security/test/csp/file_docwrite_meta.css
@@ -0,0 +1,3 @@
+body {
+ background-color: rgb(255, 0, 0);
+}
diff --git a/dom/security/test/csp/file_docwrite_meta.html b/dom/security/test/csp/file_docwrite_meta.html
new file mode 100644
index 000000000..292de3bec
--- /dev/null
+++ b/dom/security/test/csp/file_docwrite_meta.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 663570 - Test doc.write(meta csp)</title>
+ <meta charset="utf-8">
+
+ <!-- Use doc.write() to apply meta csp -->
+ <script type="application/javascript">
+ var metaCSP = "style-src 'none'; script-src 'none'; img-src 'none'";
+ document.write("<meta http-equiv=\"Content-Security-Policy\" content=\" " + metaCSP + "\">");
+ </script>
+
+ <!-- try to load a css which is forbidden by meta CSP -->
+ <link rel="stylesheet" type="text/css" href="file_docwrite_meta.css">
+
+ <!-- try to load a script which is forbidden by meta CSP -->
+ <script id="testscript" src="file_docwrite_meta.js"></script>
+
+</head>
+<body>
+
+ <!-- try to load an image which is forbidden by meta CSP -->
+ <img id="testimage" src="http://mochi.test:8888/tests/image/test/mochitest/blue.png"></img>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_docwrite_meta.js b/dom/security/test/csp/file_docwrite_meta.js
new file mode 100644
index 000000000..722adc235
--- /dev/null
+++ b/dom/security/test/csp/file_docwrite_meta.js
@@ -0,0 +1,3 @@
+// set a variable on the document which we can check to verify
+// whether the external script was loaded or blocked
+document.myMetaCSPScript = "external-JS-loaded";
diff --git a/dom/security/test/csp/file_dual_header_testserver.sjs b/dom/security/test/csp/file_dual_header_testserver.sjs
new file mode 100644
index 000000000..d5631e783
--- /dev/null
+++ b/dom/security/test/csp/file_dual_header_testserver.sjs
@@ -0,0 +1,46 @@
+/*
+ * Custom sjs file serving a test page using *two* CSP policies.
+ * See Bug 1036399 - Multiple CSP policies should be combined towards an intersection
+ */
+
+const TIGHT_POLICY = "default-src 'self'";
+const LOOSE_POLICY = "default-src 'self' 'unsafe-inline'";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var csp = "";
+ // deliver *TWO* comma separated policies which is in fact the same as serving
+ // to separate CSP headers (AppendPolicy is called twice).
+ if (request.queryString == "tight") {
+ // script execution will be *blocked*
+ csp = TIGHT_POLICY + ", " + LOOSE_POLICY;
+ }
+ else {
+ // script execution will be *allowed*
+ csp = LOOSE_POLICY + ", " + LOOSE_POLICY;
+ }
+ response.setHeader("Content-Security-Policy", csp, false);
+
+ // Send HTML to test allowed/blocked behaviors
+ response.setHeader("Content-Type", "text/html", false);
+
+ // generate an html file that contains a div container which is updated
+ // in case the inline script is *not* blocked by CSP.
+ var html = "<!DOCTYPE HTML>" +
+ "<html>" +
+ "<head>" +
+ "<title>Testpage for Bug 1036399</title>" +
+ "</head>" +
+ "<body>" +
+ "<div id='testdiv'>blocked</div>" +
+ "<script type='text/javascript'>" +
+ "document.getElementById('testdiv').innerHTML = 'allowed';" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+ response.write(html);
+}
diff --git a/dom/security/test/csp/file_evalscript_main.html b/dom/security/test/csp/file_evalscript_main.html
new file mode 100644
index 000000000..e83c1d9ed
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <title>CSP eval script tests</title>
+ <script type="application/javascript"
+ src="file_evalscript_main.js"></script>
+ </head>
+ <body>
+
+ Foo.
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_evalscript_main.html^headers^ b/dom/security/test/csp/file_evalscript_main.html^headers^
new file mode 100644
index 000000000..b91ba384d
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self'
diff --git a/dom/security/test/csp/file_evalscript_main.js b/dom/security/test/csp/file_evalscript_main.js
new file mode 100644
index 000000000..64ab05664
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main.js
@@ -0,0 +1,154 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+ var elt = document.createElement('div');
+ var color = passed ? "#cfc;" : "#fcc";
+ elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+ elt.innerHTML = str;
+ document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// check values for return values from blocked timeout or intervals
+var verifyZeroRetVal = (function(window) {
+ return function(val, details) {
+ logResult((val === 0 ? "PASS: " : "FAIL: ") + "Blocked interval/timeout should have zero return value; " + details, val === 0);
+ window.parent.verifyZeroRetVal(val, details);
+ };})(window);
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "ran";
+ window.parent.scriptRan(shouldrun, what, data);
+ logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+ };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+ return function(shouldrun, what, data) {
+ window._testResults[what] = "blocked";
+ window.parent.scriptBlocked(shouldrun, what, data);
+ logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+ };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+ // setTimeout(String) test -- mutate something in the window._testResults
+ // obj, then check it.
+ {
+ var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
+ function fcn_setTimeoutWithStringCheck() {
+ if (this._testResults["setTimeout(String)"] !== "ran") {
+ onevalblocked(false, "setTimeout(String)",
+ "setTimeout with a string was blocked");
+ }
+ }
+ setTimeout(fcn_setTimeoutWithStringCheck.bind(window), 10);
+ var res = setTimeout(str_setTimeoutWithStringRan, 10);
+ verifyZeroRetVal(res, "setTimeout(String)");
+ }
+
+ // setInterval(String) test -- mutate something in the window._testResults
+ // obj, then check it.
+ {
+ var str_setIntervalWithStringRan = 'onevalexecuted(false, "setInterval(String)", "setInterval with a string was enabled.");';
+ function fcn_setIntervalWithStringCheck() {
+ if (this._testResults["setInterval(String)"] !== "ran") {
+ onevalblocked(false, "setInterval(String)",
+ "setInterval with a string was blocked");
+ }
+ }
+ setTimeout(fcn_setIntervalWithStringCheck.bind(window), 10);
+ var res = setInterval(str_setIntervalWithStringRan, 10);
+ verifyZeroRetVal(res, "setInterval(String)");
+
+ // emergency cleanup, just in case.
+ if (res != 0) {
+ setTimeout(function () { clearInterval(res); }, 15);
+ }
+ }
+
+ // setTimeout(function) test -- mutate something in the window._testResults
+ // obj, then check it.
+ {
+ function fcn_setTimeoutWithFunctionRan() {
+ onevalexecuted(true, "setTimeout(function)",
+ "setTimeout with a function was enabled.")
+ }
+ function fcn_setTimeoutWithFunctionCheck() {
+ if (this._testResults["setTimeout(function)"] !== "ran") {
+ onevalblocked(true, "setTimeout(function)",
+ "setTimeout with a function was blocked");
+ }
+ }
+ setTimeout(fcn_setTimeoutWithFunctionRan.bind(window), 10);
+ setTimeout(fcn_setTimeoutWithFunctionCheck.bind(window), 10);
+ }
+
+ // eval() test -- should throw exception as per spec
+ try {
+ eval('onevalexecuted(false, "eval(String)", "eval() was enabled.");');
+ } catch (e) {
+ onevalblocked(false, "eval(String)",
+ "eval() was blocked");
+ }
+
+ // eval(foo,bar) test -- should throw exception as per spec
+ try {
+ eval('onevalexecuted(false, "eval(String,scope)", "eval() was enabled.");',1);
+ } catch (e) {
+ onevalblocked(false, "eval(String,object)",
+ "eval() with scope was blocked");
+ }
+
+ // [foo,bar].sort(eval) test -- should throw exception as per spec
+ try {
+ ['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1].sort(eval);
+ } catch (e) {
+ onevalblocked(false, "[String, obj].sort(eval)",
+ "eval() with scope via sort was blocked");
+ }
+
+ // [].sort.call([foo,bar], eval) test -- should throw exception as per spec
+ try {
+ [].sort.call(['onevalexecuted(false, "[String, obj].sort(eval)", "eval() was enabled.");',1], eval);
+ } catch (e) {
+ onevalblocked(false, "[].sort.call([String, obj], eval)",
+ "eval() with scope via sort/call was blocked");
+ }
+
+ // new Function() test -- should throw exception as per spec
+ try {
+ var fcn = new Function('onevalexecuted(false, "new Function(String)", "new Function(String) was enabled.");');
+ fcn();
+ } catch (e) {
+ onevalblocked(false, "new Function(String)",
+ "new Function(String) was blocked.");
+ }
+
+ // setTimeout(eval, 0, str)
+ {
+ // error is not catchable here, instead, we're going to side-effect
+ // 'worked'.
+ var worked = false;
+
+ setTimeout(eval, 0, 'worked = true');
+ setTimeout(function(worked) {
+ if (worked) {
+ onevalexecuted(false, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, string) was enabled.");
+ } else {
+ onevalblocked(false, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, str) was blocked.");
+ }
+ }, 0, worked);
+ }
+
+}, false);
+
+
+
diff --git a/dom/security/test/csp/file_evalscript_main_allowed.html b/dom/security/test/csp/file_evalscript_main_allowed.html
new file mode 100644
index 000000000..274972d9b
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main_allowed.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <title>CSP eval script tests</title>
+ <script type="application/javascript"
+ src="file_evalscript_main_allowed.js"></script>
+ </head>
+ <body>
+
+ Foo.
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_evalscript_main_allowed.html^headers^ b/dom/security/test/csp/file_evalscript_main_allowed.html^headers^
new file mode 100644
index 000000000..0cb5288be
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main_allowed.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-eval'
diff --git a/dom/security/test/csp/file_evalscript_main_allowed.js b/dom/security/test/csp/file_evalscript_main_allowed.js
new file mode 100644
index 000000000..69e6f7a4f
--- /dev/null
+++ b/dom/security/test/csp/file_evalscript_main_allowed.js
@@ -0,0 +1,121 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+ var elt = document.createElement('div');
+ var color = passed ? "#cfc;" : "#fcc";
+ elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+ elt.innerHTML = str;
+ document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+ return function(shouldrun, what, data) {
+ window.parent.scriptRan(shouldrun, what, data);
+ logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+ };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+ return function(shouldrun, what, data) {
+ window.parent.scriptBlocked(shouldrun, what, data);
+ logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+ };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+ // setTimeout(String) test -- should pass
+ try {
+ setTimeout('onevalexecuted(true, "setTimeout(String)", "setTimeout with a string was enabled.");', 10);
+ } catch (e) {
+ onevalblocked(true, "setTimeout(String)",
+ "setTimeout with a string was blocked");
+ }
+
+ // setTimeout(function) test -- should pass
+ try {
+ setTimeout(function() {
+ onevalexecuted(true, "setTimeout(function)",
+ "setTimeout with a function was enabled.")
+ }, 10);
+ } catch (e) {
+ onevalblocked(true, "setTimeout(function)",
+ "setTimeout with a function was blocked");
+ }
+
+ // eval() test
+ try {
+ eval('onevalexecuted(true, "eval(String)", "eval() was enabled.");');
+ } catch (e) {
+ onevalblocked(true, "eval(String)",
+ "eval() was blocked");
+ }
+
+ // eval(foo,bar) test
+ try {
+ eval('onevalexecuted(true, "eval(String,scope)", "eval() was enabled.");',1);
+ } catch (e) {
+ onevalblocked(true, "eval(String,object)",
+ "eval() with scope was blocked");
+ }
+
+ // [foo,bar].sort(eval) test
+ try {
+ ['onevalexecuted(true, "[String, obj].sort(eval)", "eval() was enabled.");',1].sort(eval);
+ } catch (e) {
+ onevalblocked(true, "[String, obj].sort(eval)",
+ "eval() with scope via sort was blocked");
+ }
+
+ // [].sort.call([foo,bar], eval) test
+ try {
+ [].sort.call(['onevalexecuted(true, "[String, obj].sort(eval)", "eval() was enabled.");',1], eval);
+ } catch (e) {
+ onevalblocked(true, "[].sort.call([String, obj], eval)",
+ "eval() with scope via sort/call was blocked");
+ }
+
+ // new Function() test
+ try {
+ var fcn = new Function('onevalexecuted(true, "new Function(String)", "new Function(String) was enabled.");');
+ fcn();
+ } catch (e) {
+ onevalblocked(true, "new Function(String)",
+ "new Function(String) was blocked.");
+ }
+
+ function checkResult() {
+ //alert(bar);
+ if (bar) {
+ onevalexecuted(true, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, string) was enabled.");
+ } else {
+ onevalblocked(true, "setTimeout(eval, 0, str)",
+ "setTimeout(eval, 0, str) was blocked.");
+ }
+ }
+
+ var bar = false;
+
+ function foo() {
+ bar = true;
+ }
+
+ window.foo = foo;
+
+ // setTimeout(eval, 0, str)
+
+ // error is not catchable here
+
+ setTimeout(eval, 0, 'window.foo();');
+
+ setTimeout(checkResult.bind(this), 0);
+
+}, false);
+
+
+
diff --git a/dom/security/test/csp/file_fontloader.sjs b/dom/security/test/csp/file_fontloader.sjs
new file mode 100644
index 000000000..06f6e752d
--- /dev/null
+++ b/dom/security/test/csp/file_fontloader.sjs
@@ -0,0 +1,58 @@
+// custom *.sjs for Bug 1195172
+// CSP: 'block-all-mixed-content'
+
+const PRE_HEAD =
+ "<!DOCTYPE HTML>" +
+ "<html><head><meta charset=\"utf-8\">" +
+ "<title>Bug 1195172 - CSP should block font from cache</title>";
+
+const CSP_BLOCK =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"font-src 'none'\">";
+
+const CSP_ALLOW =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"font-src *\">";
+
+const CSS =
+ "<style>" +
+ " @font-face {" +
+ " font-family: myFontTest;" +
+ " src: url(file_fontloader.woff);" +
+ " }" +
+ " div {" +
+ " font-family: myFontTest;" +
+ " }" +
+ "</style>";
+
+const POST_HEAD_AND_BODY =
+ "</head>" +
+ "<body>" +
+ "<div> Just testing the font </div>" +
+ "</body>" +
+ "</html>";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var queryString = request.queryString;
+
+ if (queryString == "baseline") {
+ response.write(PRE_HEAD + POST_HEAD_AND_BODY);
+ return;
+ }
+ if (queryString == "no-csp") {
+ response.write(PRE_HEAD + CSS + POST_HEAD_AND_BODY);
+ return;
+ }
+ if (queryString == "csp-block") {
+ response.write(PRE_HEAD + CSP_BLOCK + CSS + POST_HEAD_AND_BODY);
+ return;
+ }
+ if (queryString == "csp-allow") {
+ response.write(PRE_HEAD + CSP_ALLOW + CSS + POST_HEAD_AND_BODY);
+ return;
+ }
+ // we should never get here, but just in case return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_fontloader.woff b/dom/security/test/csp/file_fontloader.woff
new file mode 100644
index 000000000..fbf7390d5
--- /dev/null
+++ b/dom/security/test/csp/file_fontloader.woff
Binary files differ
diff --git a/dom/security/test/csp/file_form-action.html b/dom/security/test/csp/file_form-action.html
new file mode 100644
index 000000000..cfff156ba
--- /dev/null
+++ b/dom/security/test/csp/file_form-action.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 529697 - Test mapping of form submission to form-action</title>
+</head>
+<body>
+ <form action="submit-form">
+ <input id="submitButton" type="submit" value="Submit form">
+ </form>
+ <script type="text/javascript">
+ var submitButton = document.getElementById('submitButton');
+ submitButton.click();
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_form_action_server.sjs b/dom/security/test/csp/file_form_action_server.sjs
new file mode 100644
index 000000000..f2771d898
--- /dev/null
+++ b/dom/security/test/csp/file_form_action_server.sjs
@@ -0,0 +1,33 @@
+// Custom *.sjs file specifically for the needs of Bug 1251043
+
+const FRAME = `
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Bug 1251043 - Test form-action blocks URL</title>
+ <meta http-equiv="Content-Security-Policy" content="form-action 'none';">
+ </head>
+ <body>
+ CONTROL-TEXT
+ <form action="file_form_action_server.sjs?formsubmission" method="GET">
+ <input type="submit" id="submitButton" value="submit">
+ </form>
+ </body>
+ </html>`;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // PART 1: Return a frame including the FORM and the CSP
+ if (request.queryString === "loadframe") {
+ response.write(FRAME);
+ return;
+ }
+
+ // PART 2: We should never get here because the form
+ // should not be submitted. Just in case; return
+ // something unexpected so the test fails!
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_frameancestors.sjs b/dom/security/test/csp/file_frameancestors.sjs
new file mode 100644
index 000000000..d0a77893f
--- /dev/null
+++ b/dom/security/test/csp/file_frameancestors.sjs
@@ -0,0 +1,54 @@
+// SJS file for CSP frame ancestor mochitests
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var isPreflight = request.method == "OPTIONS";
+
+
+ //avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // grab the desired policy from the query, and then serve a page
+ if (query['csp'])
+ response.setHeader("Content-Security-Policy",
+ unescape(query['csp']),
+ false);
+ if (query['scriptedreport']) {
+ // spit back a script that records that the page loaded
+ response.setHeader("Content-Type", "text/javascript", false);
+ if (query['double'])
+ response.write('window.parent.parent.parent.postMessage({call: "frameLoaded", testname: "' + query['scriptedreport'] + '", uri: "window.location.toString()"}, "*");');
+ else
+ response.write('window.parent.parent.postMessage({call: "frameLoaded", testname: "' + query['scriptedreport'] + '", uri: "window.location.toString()"}, "*");');
+ } else if (query['internalframe']) {
+ // spit back an internal iframe (one that might be blocked)
+ response.setHeader("Content-Type", "text/html", false);
+ response.write('<html><head>');
+ if (query['double'])
+ response.write('<script src="file_frameancestors.sjs?double=1&scriptedreport=' + query['testid'] + '"></script>');
+ else
+ response.write('<script src="file_frameancestors.sjs?scriptedreport=' + query['testid'] + '"></script>');
+ response.write('</head><body>');
+ response.write(unescape(query['internalframe']));
+ response.write('</body></html>');
+ } else if (query['externalframe']) {
+ // spit back an internal iframe (one that won't be blocked, and probably
+ // has no CSP)
+ response.setHeader("Content-Type", "text/html", false);
+ response.write('<html><head>');
+ response.write('</head><body>');
+ response.write(unescape(query['externalframe']));
+ response.write('</body></html>');
+ } else {
+ // default case: error.
+ response.setHeader("Content-Type", "text/html", false);
+ response.write('<html><body>');
+ response.write("ERROR: not sure what to serve.");
+ response.write('</body></html>');
+ }
+}
diff --git a/dom/security/test/csp/file_frameancestors_main.html b/dom/security/test/csp/file_frameancestors_main.html
new file mode 100644
index 000000000..97f9cb9ac
--- /dev/null
+++ b/dom/security/test/csp/file_frameancestors_main.html
@@ -0,0 +1,44 @@
+<html>
+ <head>
+ <title>CSP frame ancestors tests</title>
+
+ <!-- this page shouldn't have a CSP, just the sub-pages. -->
+ <script src='file_frameancestors_main.js'></script>
+
+ </head>
+ <body>
+
+<!-- These iframes will get populated by the attached javascript. -->
+<tt> aa_allow: /* innermost frame allows a */</tt><br/>
+<iframe id='aa_allow'></iframe><br/>
+
+<tt> aa_block: /* innermost frame denies a */</tt><br/>
+<iframe id='aa_block'></iframe><br/>
+
+<tt> ab_allow: /* innermost frame allows a */</tt><br/>
+<iframe id='ab_allow'></iframe><br/>
+
+<tt> ab_block: /* innermost frame denies a */</tt><br/>
+<iframe id='ab_block'></iframe><br/>
+
+<tt> aba_allow: /* innermost frame allows b,a */</tt><br/>
+<iframe id='aba_allow'></iframe><br/>
+
+<tt> aba_block: /* innermost frame denies b */</tt><br/>
+<iframe id='aba_block'></iframe><br/>
+
+<tt> aba2_block: /* innermost frame denies a */</tt><br/>
+<iframe id='aba2_block'></iframe><br/>
+
+<tt> abb_allow: /* innermost frame allows b,a */</tt><br/>
+<iframe id='abb_allow'></iframe><br/>
+
+<tt> abb_block: /* innermost frame denies b */</tt><br/>
+<iframe id='abb_block'></iframe><br/>
+
+<tt> abb2_block: /* innermost frame denies a */</tt><br/>
+<iframe id='abb2_block'></iframe><br/>
+
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_frameancestors_main.js b/dom/security/test/csp/file_frameancestors_main.js
new file mode 100644
index 000000000..caffc7257
--- /dev/null
+++ b/dom/security/test/csp/file_frameancestors_main.js
@@ -0,0 +1,65 @@
+// Script to populate the test frames in the frame ancestors mochitest.
+//
+function setupFrames() {
+
+ var $ = function(v) { return document.getElementById(v); }
+ var base = {
+ self: '/tests/dom/security/test/csp/file_frameancestors.sjs',
+ a: 'http://mochi.test:8888/tests/dom/security/test/csp/file_frameancestors.sjs',
+ b: 'http://example.com/tests/dom/security/test/csp/file_frameancestors.sjs'
+ };
+
+ var host = { a: 'http://mochi.test:8888', b: 'http://example.com:80' };
+
+ var innerframeuri = null;
+ var elt = null;
+
+ elt = $('aa_allow');
+ elt.src = base.a + "?testid=aa_allow&internalframe=aa_a&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+
+ elt = $('aa_block');
+ elt.src = base.a + "?testid=aa_block&internalframe=aa_b&csp=" +
+ escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
+
+ elt = $('ab_allow');
+ elt.src = base.b + "?testid=ab_allow&internalframe=ab_a&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+
+ elt = $('ab_block');
+ elt.src = base.b + "?testid=ab_block&internalframe=ab_b&csp=" +
+ escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
+
+ /* .... two-level framing */
+ elt = $('aba_allow');
+ innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba_a&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + " " + host.b + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+ elt = $('aba_block');
+ innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba_b&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+ elt = $('aba2_block');
+ innerframeuri = base.a + "?testid=aba_allow&double=1&internalframe=aba2_b&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.b + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+ elt = $('abb_allow');
+ innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb_a&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + " " + host.b + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+ elt = $('abb_block');
+ innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb_b&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+
+ elt = $('abb2_block');
+ innerframeuri = base.b + "?testid=abb_allow&double=1&internalframe=abb2_b&csp=" +
+ escape("default-src 'none'; frame-ancestors " + host.b + "; script-src 'self'");
+ elt.src = base.b + "?externalframe=" + escape('<iframe src="' + innerframeuri + '"></iframe>');
+}
+
+window.addEventListener('load', setupFrames, false);
diff --git a/dom/security/test/csp/file_hash_source.html b/dom/security/test/csp/file_hash_source.html
new file mode 100644
index 000000000..47eba6cf3
--- /dev/null
+++ b/dom/security/test/csp/file_hash_source.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<html>
+ <body>
+ <!-- inline scripts -->
+ <p id="inline-script-valid-hash">blocked</p>
+ <p id="inline-script-invalid-hash">blocked</p>
+ <p id="inline-script-invalid-hash-valid-nonce">blocked</p>
+ <p id="inline-script-valid-hash-invalid-nonce">blocked</p>
+ <p id="inline-script-invalid-hash-invalid-nonce">blocked</p>
+ <p id="inline-script-valid-sha512-hash">blocked</p>
+ <p id="inline-script-valid-sha384-hash">blocked</p>
+ <p id="inline-script-valid-sha1-hash">blocked</p>
+ <p id="inline-script-valid-md5-hash">blocked</p>
+
+ <!-- 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI=' (in policy) -->
+ <script>document.getElementById("inline-script-valid-hash").innerHTML = "allowed";</script>
+ <!-- 'sha256-cYPTF2pm0QeyDtbmJ3+xi00o2Rxrw7vphBoHgOg9EnQ=' (not in policy) -->
+ <script>document.getElementById("inline-script-invalid-hash").innerHTML = "allowed";</script>
+ <!-- 'sha256-SKtBKyfeMjBpOujES0etR9t/cklbouJu/3T4PXnjbIo=' (not in policy) -->
+ <script nonce="jPRxvuRHbiQnCWVuoCMAvQ==">document.getElementById("inline-script-invalid-hash-valid-nonce").innerHTML = "allowed";</script>
+ <!-- 'sha256-z7rzCkbOJqi08lga3CVQ3b+3948ZbJWaSxsBs8zPliE=' -->
+ <script nonce="foobar">document.getElementById("inline-script-valid-hash-invalid-nonce").innerHTML = "allowed";</script>
+ <!-- 'sha256-E5TX2PmYZ4YQOK/F3XR1wFcvFjbO7QHMmxHTT/18LbE=' (not in policy) -->
+ <script nonce="foobar">document.getElementById("inline-script-invalid-hash-invalid-nonce").innerHTML = "allowed";</script>
+ <!-- 'sha512-tMLuv22jJ5RHkvLNlv0otvA2fgw6PF16HKu6wy0ZDQ3M7UKzoygs1uxIMSfjMttgWrB5WRvIr35zrTZppMYBVw==' (in policy) -->
+ <script>document.getElementById("inline-script-valid-sha512-hash").innerHTML = "allowed";</script>
+ <!-- 'sha384-XjAD+FxZfipkxna4id1JrR2QP6OYUZfAxpn9+yHOmT1VSLVa9SQR/dz7CEb7jw7w' (in policy) -->
+ <script>document.getElementById("inline-script-valid-sha384-hash").innerHTML = "allowed";</script>
+ <!-- 'sha1-LHErkMxKGcSpa/znpzmKYkKnI30=' (in policy) -->
+ <script>document.getElementById("inline-script-valid-sha1-hash").innerHTML = "allowed";</script>
+ <!-- 'md5-/m4wX3YU+IHs158KwKOBWg==' (in policy) -->
+ <script>document.getElementById("inline-script-valid-md5-hash").innerHTML = "allowed";</script>
+
+ <!-- inline styles -->
+ <p id="inline-style-valid-hash"></p>
+ <p id="inline-style-invalid-hash"></p>
+ <p id="inline-style-invalid-hash-valid-nonce"></p>
+ <p id="inline-style-valid-hash-invalid-nonce"></p>
+ <p id="inline-style-invalid-hash-invalid-nonce"></p>
+ <p id="inline-style-valid-sha512-hash"></p>
+ <p id="inline-style-valid-sha384-hash"></p>
+ <p id="inline-style-valid-sha1-hash"></p>
+ <p id="inline-style-valid-md5-hash"></p>
+
+ <!-- 'sha256-UpNH6x+Ux99QTW1fJikQsVbBERJruIC98et0YDVKKHQ=' (in policy) -->
+ <style>p#inline-style-valid-hash { color: green; }</style>
+ <!-- 'sha256-+TYxTx+bsfTDdivWLZUwScEYyxuv6lknMbNjrgGBRZo=' (not in policy) -->
+ <style>p#inline-style-invalid-hash { color: red; }</style>
+ <!-- 'sha256-U+9UPC/CFzz3QuOrl5q3KCVNngOYWuIkE2jK6Ir0Mbs=' (not in policy) -->
+ <style nonce="ftL2UbGHlSEaZTLWMwtA5Q==">p#inline-style-invalid-hash-valid-nonce { color: green; }</style>
+ <!-- 'sha256-0IPbWW5IDJ/juvETq60oTnhC+XzOqdYp5/UBsBKCaOY=' (in policy) -->
+ <style nonce="foobar">p#inline-style-valid-hash-invalid-nonce { color: green; }</style>
+ <!-- 'sha256-KaHZgPd4nC4S8BVLT/9WjzdPDtunGWojR83C2whbd50=' (not in policy) -->
+ <style nonce="foobar">p#inline-style-invalid-hash-invalid-nonce { color: red; }</style>
+ <!-- 'sha512-EpcDbSuvFv0HIyKtU5tQMN7UtBMeEbljz1dWPfy7PNCa1RYdHKwdJWT1tie41evq/ZUL1rzadSVdEzq3jl6Twg==' (in policy) -->
+ <style>p#inline-style-valid-sha512-hash { color: green; }</style>
+ <!-- 'sha384-c5W8ON4WyeA2zEOGdrOGhRmRYI8+2UzUUmhGQFjUFP6yiPZx9FGEV3UOiQ+tIshF' (in policy) -->
+ <style>p#inline-style-valid-sha384-hash { color: green; }</style>
+ <!-- 'sha1-T/+b4sxCIiJxDr6XS9dAEyHKt2M=' (in policy) -->
+ <style>p#inline-style-valid-sha1-hash { color: red; }</style>
+ <!-- 'md5-oNrgrtzOZduwDYYi1yo12g==' (in policy) -->
+ <style>p#inline-style-valid-md5-hash { color: red; }</style>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_hash_source.html^headers^ b/dom/security/test/csp/file_hash_source.html^headers^
new file mode 100644
index 000000000..785d63391
--- /dev/null
+++ b/dom/security/test/csp/file_hash_source.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI=' 'nonce-jPRxvuRHbiQnCWVuoCMAvQ==' 'sha256-z7rzCkbOJqi08lga3CVQ3b+3948ZbJWaSxsBs8zPliE=' 'sha512-tMLuv22jJ5RHkvLNlv0otvA2fgw6PF16HKu6wy0ZDQ3M7UKzoygs1uxIMSfjMttgWrB5WRvIr35zrTZppMYBVw==' 'sha384-XjAD+FxZfipkxna4id1JrR2QP6OYUZfAxpn9+yHOmT1VSLVa9SQR/dz7CEb7jw7w' 'sha1-LHErkMxKGcSpa/znpzmKYkKnI30=' 'md5-/m4wX3YU+IHs158KwKOBWg=='; style-src 'sha256-UpNH6x+Ux99QTW1fJikQsVbBERJruIC98et0YDVKKHQ=' 'nonce-ftL2UbGHlSEaZTLWMwtA5Q==' 'sha256-0IPbWW5IDJ/juvETq60oTnhC+XzOqdYp5/UBsBKCaOY=' 'sha512-EpcDbSuvFv0HIyKtU5tQMN7UtBMeEbljz1dWPfy7PNCa1RYdHKwdJWT1tie41evq/ZUL1rzadSVdEzq3jl6Twg==' 'sha384-c5W8ON4WyeA2zEOGdrOGhRmRYI8+2UzUUmhGQFjUFP6yiPZx9FGEV3UOiQ+tIshF' 'sha1-T/+b4sxCIiJxDr6XS9dAEyHKt2M=' 'md5-oNrgrtzOZduwDYYi1yo12g==';
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_iframe_sandbox_document_write.html b/dom/security/test/csp/file_iframe_sandbox_document_write.html
new file mode 100644
index 000000000..cdfa87c8f
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_document_write.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+ function doStuff() {
+ var beforePrincipal = SpecialPowers.wrap(document).nodePrincipal;
+ document.open();
+ document.write("rewritten sandboxed document");
+ document.close();
+ var afterPrincipal = SpecialPowers.wrap(document).nodePrincipal;
+ ok(beforePrincipal.equals(afterPrincipal),
+ "document.write() does not change underlying principal");
+ }
+</script>
+<body onLoad='doStuff();'>
+ sandboxed with allow-scripts
+</body>
+</html>
diff --git a/dom/security/test/csp/file_iframe_sandbox_srcdoc.html b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html
new file mode 100644
index 000000000..bc700ed68
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
+</head>
+<body>
+<iframe srcdoc="<img src=x onerror='parent.postMessage({result: `unexpected-csp-violation`}, `*`);'>"
+ sandbox="allow-scripts"></iframe>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^
new file mode 100644
index 000000000..cf869e07d
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_sandbox_srcdoc.html^headers^
@@ -0,0 +1 @@
+content-security-policy: default-src *;
diff --git a/dom/security/test/csp/file_iframe_srcdoc.sjs b/dom/security/test/csp/file_iframe_srcdoc.sjs
new file mode 100644
index 000000000..6de8a029e
--- /dev/null
+++ b/dom/security/test/csp/file_iframe_srcdoc.sjs
@@ -0,0 +1,79 @@
+// Custom *.sjs file specifically for the needs of
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1073952
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const SCRIPT = `
+ <script>
+ parent.parent.postMessage({result: &quot;allowed&quot;}, &quot;*&quot;);
+ </script>`;
+
+const SIMPLE_IFRAME_SRCDOC = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + SCRIPT + `"></iframe>
+ </body>
+ </html>`;
+
+const INNER_SRCDOC_IFRAME = `
+ <iframe sandbox='allow-scripts' srcdoc='<script>
+ parent.parent.parent.postMessage({result: &quot;allowed&quot;}, &quot;*&quot;);
+ </script>'>
+ </iframe>`;
+
+const NESTED_IFRAME_SRCDOC = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + INNER_SRCDOC_IFRAME + `"></iframe>
+ </body>
+ </html>`;
+
+
+const INNER_DATAURI_IFRAME = `
+ <iframe sandbox='allow-scripts' src='data:text/html,<script>
+ parent.parent.parent.postMessage({result: &quot;allowed&quot;}, &quot;*&quot;);
+ </script>'>
+ </iframe>`;
+
+const NESTED_IFRAME_SRCDOC_DATAURI = `
+ <!DOCTYPE html>
+ <html>
+ <head><meta charset="utf-8"></head>
+ <body>
+ <iframe sandbox="allow-scripts" srcdoc="` + INNER_DATAURI_IFRAME + `"></iframe>
+ </body>
+ </html>`;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ if (typeof query.get("csp") === "string") {
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+ response.setHeader("Content-Type", "text/html", false);
+
+ if (query.get("action") === "simple_iframe_srcdoc") {
+ response.write(SIMPLE_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc") {
+ response.write(NESTED_IFRAME_SRCDOC);
+ return;
+ }
+
+ if (query.get("action") === "nested_iframe_srcdoc_datauri") {
+ response.write(NESTED_IFRAME_SRCDOC_DATAURI);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_ignore_unsafe_inline.html b/dom/security/test/csp/file_ignore_unsafe_inline.html
new file mode 100644
index 000000000..b0d44b570
--- /dev/null
+++ b/dom/security/test/csp/file_ignore_unsafe_inline.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specified</title>
+</head>
+<body>
+<div id="testdiv">a</div>
+
+<!-- first script whitelisted by 'unsafe-inline' -->
+<script type="application/javascript">
+document.getElementById('testdiv').innerHTML += 'b';
+</script>
+
+<!-- second script whitelisted by hash -->
+<!-- sha256-uJXAPKP5NZxnVMZMUkDofh6a9P3UMRc1CRTevVPS/rI= -->
+<script type="application/javascript">
+document.getElementById('testdiv').innerHTML += 'c';
+</script>
+
+<!-- thrid script whitelisted by nonce -->
+<script type="application/javascript" nonce="FooNonce">
+document.getElementById('testdiv').innerHTML += 'd';
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_ignore_unsafe_inline_multiple_policies_server.sjs b/dom/security/test/csp/file_ignore_unsafe_inline_multiple_policies_server.sjs
new file mode 100644
index 000000000..0f0a8ad3e
--- /dev/null
+++ b/dom/security/test/csp/file_ignore_unsafe_inline_multiple_policies_server.sjs
@@ -0,0 +1,56 @@
+// custom *.sjs file specifically for the needs of:
+// * Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specified
+// * Bug 1198422: should not block inline script if default-src is not specified
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function loadHTMLFromFile(path) {
+ // Load the HTML to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ var testHTMLFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testHTMLFile.append(dirs[i]);
+ }
+ var testHTMLFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+ var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
+ return testHTML;
+}
+
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var csp1 = (query['csp1']) ? unescape(query['csp1']) : "";
+ var csp2 = (query['csp2']) ? unescape(query['csp2']) : "";
+ var file = unescape(query['file']);
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // deliver the CSP encoded in the URI
+ // please note that comma separation of two policies
+ // acts like sending *two* separate policies
+ var csp = csp1;
+ if (csp2 !== "") {
+ csp += ", " + csp2;
+ }
+ response.setHeader("Content-Security-Policy", csp, false);
+
+ // Send HTML to test allowed/blocked behaviors
+ response.setHeader("Content-Type", "text/html", false);
+
+ response.write(loadHTMLFromFile(file));
+}
diff --git a/dom/security/test/csp/file_inlinescript.html b/dom/security/test/csp/file_inlinescript.html
new file mode 100644
index 000000000..55a9b9b18
--- /dev/null
+++ b/dom/security/test/csp/file_inlinescript.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <title>CSP inline script tests</title>
+</head>
+<body onload="window.parent.postMessage('body-onload-fired', '*')">
+ <script type="text/javascript">
+ window.parent.postMessage("text-node-fired", "*");
+ </script>
+
+ <iframe src='javascript:window.parent.parent.postMessage("javascript-uri-fired", "*")'></iframe>
+
+ <a id='anchortoclick' href='javascript:window.parent.postMessage("javascript-uri-anchor-fired", "*")'>testlink</a>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_inlinestyle_main.html b/dom/security/test/csp/file_inlinestyle_main.html
new file mode 100644
index 000000000..a0d296988
--- /dev/null
+++ b/dom/security/test/csp/file_inlinestyle_main.html
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<html>
+ <head>
+ <title>CSP inline script tests</title>
+ <!-- content= "div#linkstylediv { color: #0f0; }" -->
+ <link rel="stylesheet" type="text/css"
+ href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
+ <!-- content= "div#modifycsstextdiv { color: #0f0; }" -->
+ <link rel="stylesheet" type="text/css"
+ href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
+ <script>
+ function cssTest() {
+ var elem = document.getElementById('csstextstylediv');
+ elem.style.cssText = "color: #00FF00;";
+ getComputedStyle(elem, null).color;
+
+ document.styleSheets[1].cssRules[0].style.cssText = "color: #00FF00;";
+ elem = document.getElementById('modifycsstextdiv');
+ getComputedStyle(elem, null).color;
+ }
+ </script>
+ </head>
+ <body onload='cssTest()'>
+
+ <style type="text/css">
+ div#inlinestylediv {
+ color: #FF0000;
+ }
+ </style>
+
+ <div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
+ <div id='inlinestylediv'>Inline stylesheet test (should be black)</div>
+ <div id='attrstylediv' style="color: #FF0000;">Attribute stylesheet test (should be black)</div>
+ <div id='csstextstylediv'>cssText test (should be black)</div>
+ <div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
+
+ <!-- tests for SMIL stuff - animations -->
+ <svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="100%"
+ height="100px">
+
+ <!-- Animates XML attribute, which is mapped into style. -->
+ <text id="xmlTest" x="0" y="15">
+ This shouldn't be red since the animation should be blocked by CSP.
+
+ <animate attributeName="fill" attributeType="XML"
+ values="red;orange;red" dur="2s"
+ repeatCount="indefinite" />
+ </text>
+
+ <!-- Animates override value for CSS property. -->
+ <text id="cssOverrideTest" x="0" y="35">
+ This shouldn't be red since the animation should be blocked by CSP.
+
+ <animate attributeName="fill" attributeType="CSS"
+ values="red;orange;red" dur="2s"
+ repeatCount="indefinite" />
+ </text>
+
+ <!-- Animates override value for CSS property targeted via ID. -->
+ <text id="cssOverrideTestById" x="0" y="55">
+ This shouldn't be red since the animation should be blocked by CSP.
+ </text>
+ <animate xlink:href="#cssOverrideTestById"
+ attributeName="fill"
+ values="red;orange;red"
+ dur="2s" repeatCount="indefinite" />
+
+ <!-- Sets value for CSS property targeted via ID. -->
+ <text id="cssSetTestById" x="0" y="75">
+ This shouldn't be red since the &lt;set&gt; should be blocked by CSP.
+ </text>
+ <set xlink:href="#cssSetTestById"
+ attributeName="fill"
+ to="red" />
+ </svg>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_inlinestyle_main.html^headers^ b/dom/security/test/csp/file_inlinestyle_main.html^headers^
new file mode 100644
index 000000000..7b6a25167
--- /dev/null
+++ b/dom/security/test/csp/file_inlinestyle_main.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline'
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_inlinestyle_main_allowed.html b/dom/security/test/csp/file_inlinestyle_main_allowed.html
new file mode 100644
index 000000000..9b533ef07
--- /dev/null
+++ b/dom/security/test/csp/file_inlinestyle_main_allowed.html
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<html>
+ <head>
+ <title>CSP inline script tests</title>
+ <!-- content= "div#linkstylediv { color: #0f0; }" -->
+ <link rel="stylesheet" type="text/css"
+ href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
+ <!-- content= "div#modifycsstextdiv { color: #f00; }" -->
+ <link rel="stylesheet" type="text/css"
+ href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
+ <script>
+ function cssTest() {
+ // CSSStyleDeclaration.cssText
+ var elem = document.getElementById('csstextstylediv');
+ elem.style.cssText = "color: #00FF00;";
+
+ // If I call getComputedStyle as below, this test passes as the parent page
+ // correctly detects that the text is colored green - if I remove this, getComputedStyle
+ // thinks the text is black when called by the parent page.
+ getComputedStyle(elem, null).color;
+
+ document.styleSheets[1].cssRules[0].style.cssText = "color: #00FF00;";
+ elem = document.getElementById('modifycsstextdiv');
+ getComputedStyle(elem, null).color;
+ }
+ </script>
+ </head>
+ <body onload='cssTest()'>
+
+ <style type="text/css">
+ div#inlinestylediv {
+ color: #00FF00;
+ }
+ </style>
+
+ <div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
+ <div id='inlinestylediv'>Inline stylesheet test (should be green)</div>
+ <div id='attrstylediv' style="color: #00FF00;">Attribute stylesheet test (should be green)</div>
+ <div id='csstextstylediv'>style.cssText test (should be green)</div>
+ <div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
+
+ <!-- tests for SMIL stuff - animations -->
+ <svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="100%"
+ height="100px">
+
+ <!-- Animates XML attribute, which is mapped into style. -->
+ <text id="xmlTest" x="0" y="15">
+ This should be green since the animation should be allowed by CSP.
+
+ <animate attributeName="fill" attributeType="XML"
+ values="lime;green;lime" dur="2s"
+ repeatCount="indefinite" />
+ </text>
+
+ <!-- Animates override value for CSS property. -->
+ <text id="cssOverrideTest" x="0" y="35">
+ This should be green since the animation should be allowed by CSP.
+
+ <animate attributeName="fill" attributeType="CSS"
+ values="lime;green;lime" dur="2s"
+ repeatCount="indefinite" />
+ </text>
+
+ <!-- Animates override value for CSS property targeted via ID. -->
+ <text id="cssOverrideTestById" x="0" y="55">
+ This should be green since the animation should be allowed by CSP.
+ </text>
+ <animate xlink:href="#cssOverrideTestById"
+ attributeName="fill"
+ values="lime;green;lime"
+ dur="2s" repeatCount="indefinite" />
+
+ <!-- Sets value for CSS property targeted via ID. -->
+ <text id="cssSetTestById" x="0" y="75">
+ This should be green since the &lt;set&gt; should be allowed by CSP.
+ </text>
+ <set xlink:href="#cssSetTestById"
+ attributeName="fill"
+ to="lime" />
+ </svg>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_inlinestyle_main_allowed.html^headers^ b/dom/security/test/csp/file_inlinestyle_main_allowed.html^headers^
new file mode 100644
index 000000000..621d2536b
--- /dev/null
+++ b/dom/security/test/csp/file_inlinestyle_main_allowed.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline' ; style-src 'self' 'unsafe-inline'
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_invalid_source_expression.html b/dom/security/test/csp/file_invalid_source_expression.html
new file mode 100644
index 000000000..83bb0ec0c
--- /dev/null
+++ b/dom/security/test/csp/file_invalid_source_expression.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1086612 - CSP: Let source expression be the empty set in case no valid source can be parsed</title>
+ </head>
+ <body>
+ <div id="testdiv">blocked</div>
+ <!-- Note, we reuse file_path_matching.js which only updates the testdiv to 'allowed' if loaded !-->
+ <script src="http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_leading_wildcard.html b/dom/security/test/csp/file_leading_wildcard.html
new file mode 100644
index 000000000..ea5e99344
--- /dev/null
+++ b/dom/security/test/csp/file_leading_wildcard.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1032303 - CSP - Keep FULL STOP when matching *.foo.com to disallow loads from foo.com</title>
+ </head>
+ <body>
+ <!-- Please note that both scripts do *not* exist in the file system -->
+ <script src="http://test1.example.com/tests/dom/security/test/csp/leading_wildcard_allowed.js" ></script>
+ <script src="http://example.com/tests/dom/security/test/csp/leading_wildcard_blocked.js" ></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_main.html b/dom/security/test/csp/file_main.html
new file mode 100644
index 000000000..ddc838261
--- /dev/null
+++ b/dom/security/test/csp/file_main.html
@@ -0,0 +1,55 @@
+<html>
+ <head>
+ <link rel='stylesheet' type='text/css'
+ href='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=style_bad&type=text/css' />
+ <link rel='stylesheet' type='text/css'
+ href='file_CSP.sjs?testid=style_good&type=text/css' />
+
+
+ <style>
+ /* CSS font embedding tests */
+ @font-face {
+ font-family: "arbitrary_good";
+ src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
+ }
+ @font-face {
+ font-family: "arbitrary_bad";
+ src: url('http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
+ }
+
+ .div_arbitrary_good { font-family: "arbitrary_good"; }
+ .div_arbitrary_bad { font-family: "arbitrary_bad"; }
+ </style>
+ </head>
+ <body>
+ <!-- these should be stopped by CSP. :) -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
+ <audio src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=media_bad&type=audio/vorbis"></audio>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script_bad&type=text/javascript'></script>
+ <iframe src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=frame_bad&content=FAIL'></iframe>
+ <object width="10" height="10">
+ <param name="movie" value="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=object_bad&type=application/x-shockwave-flash">
+ <embed src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=object_bad&type=application/x-shockwave-flash"></embed>
+ </object>
+
+ <!-- these should load ok. :) -->
+ <img src="file_CSP.sjs?testid=img_good&type=img/png" />
+ <audio src="file_CSP.sjs?testid=media_good&type=audio/vorbis"></audio>
+ <script src='file_CSP.sjs?testid=script_good&type=text/javascript'></script>
+ <iframe src='file_CSP.sjs?testid=frame_good&content=PASS'></iframe>
+
+ <object width="10" height="10">
+ <param name="movie" value="file_CSP.sjs?testid=object_good&type=application/x-shockwave-flash">
+ <embed src="file_CSP.sjs?testid=object_good&type=application/x-shockwave-flash"></embed>
+ </object>
+
+ <!-- XHR tests... they're taken care of in this script,
+ and since the URI doesn't have any 'testid' values,
+ it will just be ignored by the test framework. -->
+ <script src='file_main.js'></script>
+
+ <!-- Support elements for the @font-face test -->
+ <div class="div_arbitrary_good">arbitrary good</div>
+ <div class="div_arbitrary_bad">arbitrary_bad</div>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_main.html^headers^ b/dom/security/test/csp/file_main.html^headers^
new file mode 100644
index 000000000..3338de389
--- /dev/null
+++ b/dom/security/test/csp/file_main.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self' blob: ; style-src 'unsafe-inline' 'self'
diff --git a/dom/security/test/csp/file_main.js b/dom/security/test/csp/file_main.js
new file mode 100644
index 000000000..d5afe580f
--- /dev/null
+++ b/dom/security/test/csp/file_main.js
@@ -0,0 +1,51 @@
+function doXHR(uri, callback) {
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", uri);
+ xhr.responseType = "blob";
+ xhr.send();
+ xhr.onload = function () {
+ if (callback) callback(xhr.response);
+ }
+ } catch(ex) {}
+}
+
+doXHR("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_good");
+doXHR("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=xhr_bad");
+fetch("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_good");
+fetch("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=fetch_bad");
+navigator.sendBeacon("http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_good");
+navigator.sendBeacon("http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=beacon_bad");
+
+var topWorkerBlob;
+var nestedWorkerBlob;
+
+doXHR("file_main_worker.js", function (topResponse) {
+ topWorkerBlob = URL.createObjectURL(topResponse);
+ doXHR("file_child_worker.js", function (response) {
+ nestedWorkerBlob = URL.createObjectURL(response);
+ runWorker();
+ });
+});
+
+function runWorker() {
+ // Top level worker, no subworker
+ // Worker does not inherit CSP from owner document
+ new Worker("file_main_worker.js").postMessage({inherited : "none"});
+
+ // Top level worker, no subworker
+ // Worker inherits CSP from owner document
+ new Worker(topWorkerBlob).postMessage({inherited : "document"});
+
+ // Subworker
+ // Worker does not inherit CSP from parent worker
+ new Worker("file_main_worker.js").postMessage({inherited : "none", nested : nestedWorkerBlob});
+
+ // Subworker
+ // Worker inherits CSP from parent worker
+ new Worker("file_main_worker.js").postMessage({inherited : "parent", nested : nestedWorkerBlob});
+
+ // Subworker
+ // Worker inherits CSP from owner document -> parent worker -> subworker
+ new Worker(topWorkerBlob).postMessage({inherited : "document", nested : nestedWorkerBlob});
+}
diff --git a/dom/security/test/csp/file_main_worker.js b/dom/security/test/csp/file_main_worker.js
new file mode 100644
index 000000000..d1314c843
--- /dev/null
+++ b/dom/security/test/csp/file_main_worker.js
@@ -0,0 +1,48 @@
+function doXHR(uri) {
+ try {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", uri);
+ xhr.send();
+ } catch(ex) {}
+}
+
+var sameBase = "http://mochi.test:8888/tests/dom/security/test/csp/file_CSP.sjs?testid=";
+var crossBase = "http://example.com/tests/dom/security/test/csp/file_CSP.sjs?testid=";
+
+onmessage = (e) => {
+ // Tests of nested worker
+ if (e.data.nested) {
+ if (e.data.inherited != "none") {
+ // Worker inherits CSP
+ new Worker(e.data.nested).postMessage({inherited : e.data.inherited});
+ }
+ else {
+ // Worker does not inherit CSP
+ new Worker("file_child_worker.js").postMessage({inherited : e.data.inherited});
+ }
+ return;
+ }
+
+ //Tests of top level worker
+ for (base of [sameBase, crossBase]) {
+ var prefix;
+ var suffix;
+ if (e.data.inherited != "none") {
+ // Top worker inherits CSP from owner document
+ prefix = base + "worker_inherited_";
+ suffix = base == sameBase ? "_good" : "_bad";
+ }
+ else {
+ // Top worker delivers CSP from HTTP header
+ prefix = base + "worker_";
+ suffix = base == sameBase ? "_same_bad" : "_cross_good";
+ }
+
+ doXHR(prefix + "xhr" + suffix);
+ fetch(prefix + "fetch" + suffix);
+ try {
+ if (e.data.inherited == "none") suffix = base == sameBase ? "_same_good" : "_cross_bad";
+ importScripts(prefix + "script" + suffix);
+ } catch(ex) {}
+ }
+}
diff --git a/dom/security/test/csp/file_main_worker.js^headers^ b/dom/security/test/csp/file_main_worker.js^headers^
new file mode 100644
index 000000000..3f5008ca6
--- /dev/null
+++ b/dom/security/test/csp/file_main_worker.js^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self' blob: ; connect-src http://example.com
diff --git a/dom/security/test/csp/file_meta_element.html b/dom/security/test/csp/file_meta_element.html
new file mode 100644
index 000000000..1d8ec6aa9
--- /dev/null
+++ b/dom/security/test/csp/file_meta_element.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy"
+ content= "img-src 'none'; script-src 'unsafe-inline'; report-uri http://www.example.com; frame-ancestors https:; sandbox allow-scripts">
+ <title>Bug 663570 - Implement Content Security Policy via meta tag</title>
+</head>
+<body>
+
+ <!-- try to load an image which is forbidden by meta CSP -->
+ <img id="testimage" src="http://mochi.test:8888/tests/image/test/mochitest/blue.png"></img>
+
+ <script type="application/javascript">
+ var myImg = document.getElementById("testimage");
+ myImg.onload = function(e) {
+ window.parent.postMessage({result: "img-loaded"}, "*");
+ };
+ myImg.onerror = function(e) {
+ window.parent.postMessage({result: "img-blocked"}, "*");
+ };
+ </script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_meta_header_dual.sjs b/dom/security/test/csp/file_meta_header_dual.sjs
new file mode 100644
index 000000000..ddc38ffe5
--- /dev/null
+++ b/dom/security/test/csp/file_meta_header_dual.sjs
@@ -0,0 +1,98 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 663570 - Implement Content Security Policy via meta tag
+
+const HTML_HEAD =
+ "<!DOCTYPE HTML>" +
+ "<html>" +
+ "<head>" +
+ "<meta charset='utf-8'>" +
+ "<title>Bug 663570 - Implement Content Security Policy via <meta> tag</title>";
+
+const HTML_BODY =
+ "</head>" +
+ "<body>" +
+ "<img id='testimage' src='http://mochi.test:8888/tests/image/test/mochitest/blue.png'></img>" +
+ "<script type='application/javascript'>" +
+ " var myImg = document.getElementById('testimage');" +
+ " myImg.onload = function(e) {" +
+ " window.parent.postMessage({result: 'img-loaded'}, '*');" +
+ " };" +
+ " myImg.onerror = function(e) { " +
+ " window.parent.postMessage({result: 'img-blocked'}, '*');" +
+ " };" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+const META_CSP_BLOCK_IMG =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"img-src 'none'\">";
+
+const META_CSP_ALLOW_IMG =
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"img-src http://mochi.test:8888;\">";
+
+const HEADER_CSP_BLOCK_IMG = "img-src 'none';";
+
+const HEADER_CSP_ALLOW_IMG = "img-src http://mochi.test:8888";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+ var queryString = request.queryString;
+
+ if (queryString === "test1") {
+ /* load image without any CSP */
+ response.write(HTML_HEAD + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test2") {
+ /* load image where meta denies load */
+ response.write(HTML_HEAD + META_CSP_BLOCK_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test3") {
+ /* load image where meta allows load */
+ response.write(HTML_HEAD + META_CSP_ALLOW_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test4") {
+ /* load image where meta allows but header blocks */
+ response.setHeader("Content-Security-Policy", HEADER_CSP_BLOCK_IMG, false);
+ response.write(HTML_HEAD + META_CSP_ALLOW_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test5") {
+ /* load image where meta blocks but header allows */
+ response.setHeader("Content-Security-Policy", HEADER_CSP_ALLOW_IMG, false);
+ response.write(HTML_HEAD + META_CSP_BLOCK_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test6") {
+ /* load image where meta allows and header allows */
+ response.setHeader("Content-Security-Policy", HEADER_CSP_ALLOW_IMG, false);
+ response.write(HTML_HEAD + META_CSP_ALLOW_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test7") {
+ /* load image where meta1 allows but meta2 blocks */
+ response.write(HTML_HEAD + META_CSP_ALLOW_IMG + META_CSP_BLOCK_IMG + HTML_BODY);
+ return;
+ }
+
+ if (queryString === "test8") {
+ /* load image where meta1 allows and meta2 allows */
+ response.write(HTML_HEAD + META_CSP_ALLOW_IMG + META_CSP_ALLOW_IMG + HTML_BODY);
+ return;
+ }
+
+ // we should never get here, but just in case, return
+ // something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_meta_whitespace_skipping.html b/dom/security/test/csp/file_meta_whitespace_skipping.html
new file mode 100644
index 000000000..c0cfc8cc2
--- /dev/null
+++ b/dom/security/test/csp/file_meta_whitespace_skipping.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <!-- Test all the different space characters within the meta csp:
+ * U+0020 space | &#x20;
+ * U+0009 tab | &#x9;
+ * U+000A line feed | &#xa;
+ * U+000C form feed | &#xc;
+ * U+000D carriage return | &#xd;
+ !-->
+ <meta http-equiv="Content-Security-Policy"
+ content= "
+ img-src&#x20; 'none'; &#x20;
+ script-src 'unsafe-inline' &#xd;
+ ;
+ style-src&#x9;&#x9; https://example.com&#xa;
+ https://foo.com;&#xc;;;;&#xc;;;&#xc; child-src foo.com
+ bar.com
+ &#xd;;&#xd;
+ font-src 'none'">
+ <title>Bug 1261634 - Update whitespace skipping for meta csp</title>
+</head>
+<body>
+ <script type="application/javascript">
+ // notify the including document that we are done parsing the meta csp
+ window.parent.postMessage({result: "meta-csp-parsed"}, "*");
+ </script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_multi_policy_injection_bypass.html b/dom/security/test/csp/file_multi_policy_injection_bypass.html
new file mode 100644
index 000000000..a3cb415a9
--- /dev/null
+++ b/dom/security/test/csp/file_multi_policy_injection_bypass.html
@@ -0,0 +1,15 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717511
+-->
+ <body>
+ <!-- these should be stopped by CSP after fixing bug 717511. :) -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script_bad&type=text/javascript'></script>
+
+ <!-- these should load ok after fixing bug 717511. :) -->
+ <img src="file_CSP.sjs?testid=img_good&type=img/png" />
+ <script src='file_CSP.sjs?testid=script_good&type=text/javascript'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_multi_policy_injection_bypass.html^headers^ b/dom/security/test/csp/file_multi_policy_injection_bypass.html^headers^
new file mode 100644
index 000000000..e1b64a922
--- /dev/null
+++ b/dom/security/test/csp/file_multi_policy_injection_bypass.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self', default-src *
diff --git a/dom/security/test/csp/file_multi_policy_injection_bypass_2.html b/dom/security/test/csp/file_multi_policy_injection_bypass_2.html
new file mode 100644
index 000000000..3fa6c7ab9
--- /dev/null
+++ b/dom/security/test/csp/file_multi_policy_injection_bypass_2.html
@@ -0,0 +1,15 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717511
+-->
+ <body>
+ <!-- these should be stopped by CSP after fixing bug 717511. :) -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script2_bad&type=text/javascript'></script>
+
+ <!-- these should load ok after fixing bug 717511. :) -->
+ <img src="file_CSP.sjs?testid=img2_good&type=img/png" />
+ <script src='file_CSP.sjs?testid=script2_good&type=text/javascript'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_multi_policy_injection_bypass_2.html^headers^ b/dom/security/test/csp/file_multi_policy_injection_bypass_2.html^headers^
new file mode 100644
index 000000000..b523073cd
--- /dev/null
+++ b/dom/security/test/csp/file_multi_policy_injection_bypass_2.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self' , default-src *
diff --git a/dom/security/test/csp/file_multipart_testserver.sjs b/dom/security/test/csp/file_multipart_testserver.sjs
new file mode 100644
index 000000000..d2eb58c82
--- /dev/null
+++ b/dom/security/test/csp/file_multipart_testserver.sjs
@@ -0,0 +1,50 @@
+// SJS file specifically for the needs of bug
+// Bug 1223743 - CSP: Check baseChannel for CSP when loading multipart channel
+
+var CSP = "script-src 'unsafe-inline', img-src 'none'";
+var BOUNDARY = "fooboundary"
+
+// small red image
+const IMG_BYTES = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
+
+var RESPONSE = `
+ <script>
+ var myImg = new Image;
+ myImg.src = "file_multipart_testserver.sjs?img";
+ myImg.onerror = function(e) {
+ window.parent.postMessage("img-blocked", "*");
+ };
+ myImg.onload = function() {
+ window.parent.postMessage("img-loaded", "*");
+ };
+ document.body.appendChild(myImg);
+ </script>
+`;
+
+var myTimer;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ if (request.queryString == "doc") {
+ response.setHeader("Content-Security-Policy", CSP, false);
+ response.setHeader("Content-Type", "multipart/x-mixed-replace; boundary=" + BOUNDARY, false);
+ response.write(BOUNDARY + "\r\n");
+ response.write(RESPONSE);
+ response.write(BOUNDARY + "\r\n");
+ return;
+ }
+
+ if (request.queryString == "img") {
+ response.setHeader("Content-Type", "image/png");
+ response.write(IMG_BYTES);
+ return;
+ }
+
+ // we should never get here - return something unexpected
+ response.write("d'oh");
+}
diff --git a/dom/security/test/csp/file_nonce_source.html b/dom/security/test/csp/file_nonce_source.html
new file mode 100644
index 000000000..bf14ffe64
--- /dev/null
+++ b/dom/security/test/csp/file_nonce_source.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+ <head>
+ <!-- external styles -->
+ <link rel='stylesheet' nonce="correctstylenonce" href="file_CSP.sjs?testid=external_style_correct_nonce_good&type=text/css" />
+ <link rel='stylesheet' nonce="incorrectstylenonce" href="file_CSP.sjs?testid=external_style_incorrect_nonce_bad&type=text/css" />
+ <link rel='stylesheet' nonce="correctscriptnonce" href="file_CSP.sjs?testid=external_style_correct_script_nonce_bad&type=text/css" />
+ <link rel='stylesheet' href="file_CSP.sjs?testid=external_style_no_nonce_bad&type=text/css" />
+ </head>
+ <body>
+ <!-- inline scripts -->
+ <ol>
+ <li id="inline-script-correct-nonce">(inline script with correct nonce) This text should be green.</li>
+ <li id="inline-script-incorrect-nonce">(inline script with incorrect nonce) This text should be black.</li>
+ <li id="inline-script-correct-style-nonce">(inline script with correct nonce for styles, but not for scripts) This text should be black.</li>
+ <li id="inline-script-no-nonce">(inline script with no nonce) This text should be black.</li>
+ </ol>
+ <script nonce="correctscriptnonce">
+ document.getElementById("inline-script-correct-nonce").style.color = "rgb(0, 128, 0)";
+ </script>
+ <script nonce="incorrectscriptnonce">
+ document.getElementById("inline-script-incorrect-nonce").style.color = "rgb(255, 0, 0)";
+ </script>
+ <script nonce="correctstylenonce">
+ document.getElementById("inline-script-correct-style-nonce").style.color = "rgb(255, 0, 0)";
+ </script>
+ <script>
+ document.getElementById("inline-script-no-nonce").style.color = "rgb(255, 0, 0)";
+ </script>
+
+ <!-- external scripts -->
+ <script nonce="correctscriptnonce" src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=external_script_correct_nonce_good&type=text/javascript"></script>
+ <script nonce="anothercorrectscriptnonce" src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=external_script_another_correct_nonce_good&type=text/javascript"></script>
+ <script nonce="incorrectscriptnonce" src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=external_script_incorrect_nonce_bad&type=text/javascript"></script>
+ <script nonce="correctstylenonce" src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=external_script_correct_style_nonce_bad&type=text/javascript"></script>
+ <script src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=external_script_no_nonce_bad&type=text/javascript"></script>
+
+ <!-- This external script has the correct nonce and comes from a whitelisted URI. It should be allowed. -->
+ <script nonce="correctscriptnonce" src="file_CSP.sjs?testid=external_script_correct_nonce_correct_uri_good&type=text/javascript"></script>
+ <!-- This external script has an incorrect nonce, but comes from a whitelisted URI. It should be allowed. -->
+ <script nonce="incorrectscriptnonce" src="file_CSP.sjs?testid=external_script_incorrect_nonce_correct_uri_good&type=text/javascript"></script>
+ <!-- This external script has no nonce and comes from a whitelisted URI. It should be allowed. -->
+ <script src="file_CSP.sjs?testid=external_script_no_nonce_correct_uri_good&type=text/javascript"></script>
+
+ <!-- inline styles -->
+ <ol>
+ <li id=inline-style-correct-nonce>
+ (inline style with correct nonce) This text should be green
+ </li>
+ <li id=inline-style-incorrect-nonce>
+ (inline style with incorrect nonce) This text should be black
+ </li>
+ <li id=inline-style-correct-script-nonce>
+ (inline style with correct script, not style, nonce) This text should be black
+ </li>
+ <li id=inline-style-no-nonce>
+ (inline style with no nonce) This text should be black
+ </li>
+ </ol>
+ <style nonce=correctstylenonce>
+ li#inline-style-correct-nonce { color: green; }
+ </style>
+ <style nonce=incorrectstylenonce>
+ li#inline-style-incorrect-nonce { color: red; }
+ </style>
+ <style nonce=correctscriptnonce>
+ li#inline-style-correct-script-nonce { color: red; }
+ </style>
+ <style>
+ li#inline-style-no-nonce { color: red; }
+ </style>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_nonce_source.html^headers^ b/dom/security/test/csp/file_nonce_source.html^headers^
new file mode 100644
index 000000000..865e5fe98
--- /dev/null
+++ b/dom/security/test/csp/file_nonce_source.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: script-src 'self' 'nonce-correctscriptnonce' 'nonce-anothercorrectscriptnonce'; style-src 'nonce-correctstylenonce';
+Cache-Control: no-cache
diff --git a/dom/security/test/csp/file_null_baseuri.html b/dom/security/test/csp/file_null_baseuri.html
new file mode 100644
index 000000000..f995688b1
--- /dev/null
+++ b/dom/security/test/csp/file_null_baseuri.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1121857 - document.baseURI should not get blocked if baseURI is null</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ // check the initial base-uri
+ window.parent.postMessage({baseURI: document.baseURI, test: "initial_base_uri"}, "*");
+
+ // append a child and check the base-uri
+ var baseTag = document.head.appendChild(document.createElement('base'));
+ baseTag.href = 'http://www.base-tag.com';
+ window.parent.postMessage({baseURI: document.baseURI, test: "changed_base_uri"}, "*");
+
+ // remove the child and check that the base-uri is back to the initial one
+ document.head.remove(baseTag);
+ window.parent.postMessage({baseURI: document.baseURI, test: "initial_base_uri"}, "*");
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_path_matching.html b/dom/security/test/csp/file_path_matching.html
new file mode 100644
index 000000000..662fbfb8a
--- /dev/null
+++ b/dom/security/test/csp/file_path_matching.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 808292 - Implement path-level host-source matching to CSP</title>
+ </head>
+ <body>
+ <div id="testdiv">blocked</div>
+ <script src="http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js#foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_path_matching.js b/dom/security/test/csp/file_path_matching.js
new file mode 100644
index 000000000..09286d42e
--- /dev/null
+++ b/dom/security/test/csp/file_path_matching.js
@@ -0,0 +1 @@
+document.getElementById("testdiv").innerHTML = "allowed";
diff --git a/dom/security/test/csp/file_path_matching_incl_query.html b/dom/security/test/csp/file_path_matching_incl_query.html
new file mode 100644
index 000000000..50af2b143
--- /dev/null
+++ b/dom/security/test/csp/file_path_matching_incl_query.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1147026 - CSP should ignore query string when checking a resource load</title>
+ </head>
+ <body>
+ <div id="testdiv">blocked</div>
+ <script src="http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js?val=foo"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_path_matching_redirect.html b/dom/security/test/csp/file_path_matching_redirect.html
new file mode 100644
index 000000000..a16cc90ec
--- /dev/null
+++ b/dom/security/test/csp/file_path_matching_redirect.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 808292 - Implement path-level host-source matching to CSP</title>
+ </head>
+ <body>
+ <div id="testdiv">blocked</div>
+ <script src="http://example.com/tests/dom/security/test/csp/file_path_matching_redirect_server.sjs"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_path_matching_redirect_server.sjs b/dom/security/test/csp/file_path_matching_redirect_server.sjs
new file mode 100644
index 000000000..49a3160f4
--- /dev/null
+++ b/dom/security/test/csp/file_path_matching_redirect_server.sjs
@@ -0,0 +1,13 @@
+// Redirect server specifically to handle redirects
+// for path-level host-source matching
+// see https://bugzilla.mozilla.org/show_bug.cgi?id=808292
+
+function handleRequest(request, response)
+{
+
+ var newLocation = "http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js";
+
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Location", newLocation, false);
+}
diff --git a/dom/security/test/csp/file_ping.html b/dom/security/test/csp/file_ping.html
new file mode 100644
index 000000000..8aaf34cc3
--- /dev/null
+++ b/dom/security/test/csp/file_ping.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1100181 - CSP: Enforce connect-src when submitting pings</title>
+</head>
+<body>
+ <!-- we are using an image for the test, but can be anything -->
+ <a id="testlink"
+ href="http://mochi.test:8888/tests/image/test/mochitest/blue.png"
+ ping="http://mochi.test:8888/tests/image/test/mochitest/blue.png?send-ping">
+ Send ping
+ </a>
+
+ <script type="text/javascript">
+ var link = document.getElementById("testlink");
+ link.click();
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html b/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html
new file mode 100644
index 000000000..2a75eef7e
--- /dev/null
+++ b/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+ <body>
+ <div id=testdiv>Inline script didn't run</div>
+ <script>
+ document.getElementById('testdiv').innerHTML = "Inline Script Executed";
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html^headers^ b/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html^headers^
new file mode 100644
index 000000000..c4ff8ea9f
--- /dev/null
+++ b/dom/security/test/csp/file_policyuri_regression_from_multipolicy.html^headers^
@@ -0,0 +1 @@
+content-security-policy-report-only: policy-uri /tests/dom/security/test/csp/file_policyuri_regression_from_multipolicy_policy
diff --git a/dom/security/test/csp/file_policyuri_regression_from_multipolicy_policy b/dom/security/test/csp/file_policyuri_regression_from_multipolicy_policy
new file mode 100644
index 000000000..a5c610cd7
--- /dev/null
+++ b/dom/security/test/csp/file_policyuri_regression_from_multipolicy_policy
@@ -0,0 +1 @@
+default-src 'self';
diff --git a/dom/security/test/csp/file_redirect_content.sjs b/dom/security/test/csp/file_redirect_content.sjs
new file mode 100644
index 000000000..080f179cd
--- /dev/null
+++ b/dom/security/test/csp/file_redirect_content.sjs
@@ -0,0 +1,38 @@
+// https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+// This SJS file serves file_redirect_content.html
+// with a CSP that will trigger a violation and that will report it
+// to file_redirect_report.sjs
+//
+// This handles 301, 302, 303 and 307 redirects. The HTTP status code
+// returned/type of redirect to do comes from the query string
+// parameter passed in from the test_bug650386_* files and then also
+// uses that value in the report-uri parameter of the CSP
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // this gets used in the CSP as part of the report URI.
+ var redirect = request.queryString;
+
+ if (redirect < 301 || (redirect > 303 && redirect <= 306) || redirect > 307) {
+ // if we somehow got some bogus redirect code here,
+ // do a 302 redirect to the same URL as the report URI
+ // redirects to - this will fail the test.
+ var loc = "http://example.com/some/fake/path";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+ }
+
+ var csp = "default-src \'self\';report-uri http://mochi.test:8888/tests/dom/security/test/csp/file_redirect_report.sjs?" + redirect;
+
+ response.setHeader("Content-Security-Policy", csp, false);
+
+ // the actual file content.
+ // this image load will (intentionally) fail due to the CSP policy of default-src: 'self'
+ // specified by the CSP string above.
+ var content = "<!DOCTYPE HTML><html><body><img src = \"http://some.other.domain.example.com\"></body></html>";
+
+ response.write(content);
+
+ return;
+}
diff --git a/dom/security/test/csp/file_redirect_report.sjs b/dom/security/test/csp/file_redirect_report.sjs
new file mode 100644
index 000000000..9cc7e6548
--- /dev/null
+++ b/dom/security/test/csp/file_redirect_report.sjs
@@ -0,0 +1,17 @@
+// https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+// This SJS file serves as CSP violation report target
+// and issues a redirect, to make sure the browser does not post to the target
+// of the redirect, per CSP spec.
+// This handles 301, 302, 303 and 307 redirects. The HTTP status code
+// returned/type of redirect to do comes from the query string
+// parameter
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ var redirect = request.queryString;
+
+ var loc = "http://example.com/some/fake/path";
+ response.setStatusLine("1.1", redirect, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+}
diff --git a/dom/security/test/csp/file_redirect_worker.sjs b/dom/security/test/csp/file_redirect_worker.sjs
new file mode 100644
index 000000000..27df6a5fd
--- /dev/null
+++ b/dom/security/test/csp/file_redirect_worker.sjs
@@ -0,0 +1,34 @@
+// SJS file to serve resources for CSP redirect tests
+// This file redirects to a specified resource.
+const THIS_SITE = "http://mochi.test:8888";
+const OTHER_SITE = "http://example.com";
+
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var resource = query['path'];
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ var loc = '';
+
+ // redirect to a resource on this site
+ if (query["redir"] == "same") {
+ loc = THIS_SITE+resource+"#"+query['page_id']
+ }
+
+ // redirect to a resource on a different site
+ else if (query["redir"] == "other") {
+ loc = OTHER_SITE+resource+"#"+query['page_id']
+ }
+
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+
+ response.write('<html><head><meta http-equiv="refresh" content="0; url='+loc+'">');
+ return;
+}
diff --git a/dom/security/test/csp/file_redirects_main.html b/dom/security/test/csp/file_redirects_main.html
new file mode 100644
index 000000000..d05af88fe
--- /dev/null
+++ b/dom/security/test/csp/file_redirects_main.html
@@ -0,0 +1,37 @@
+<html>
+<head>
+<title>CSP redirect tests</title>
+</head>
+<body>
+<div id="container"></div>
+</body>
+
+<script>
+var thisSite = "http://mochi.test:8888";
+var otherSite = "http://example.com";
+var page = "/tests/dom/security/test/csp/file_redirects_page.sjs";
+
+var tests = { "font-src": thisSite+page+"?testid=font-src",
+ "frame-src": thisSite+page+"?testid=frame-src",
+ "img-src": thisSite+page+"?testid=img-src",
+ "media-src": thisSite+page+"?testid=media-src",
+ "object-src": thisSite+page+"?testid=object-src",
+ "script-src": thisSite+page+"?testid=script-src",
+ "style-src": thisSite+page+"?testid=style-src",
+ "xhr-src": thisSite+page+"?testid=xhr-src",
+ "from-worker": thisSite+page+"?testid=from-worker",
+ "from-blob-worker": thisSite+page+"?testid=from-blob-worker",
+ "img-src-from-css": thisSite+page+"?testid=img-src-from-css",
+ };
+
+var container = document.getElementById("container");
+
+// load each test in its own iframe
+for (tid in tests) {
+ var i = document.createElement("iframe");
+ i.id = tid;
+ i.src = tests[tid];
+ container.appendChild(i);
+}
+</script>
+</html>
diff --git a/dom/security/test/csp/file_redirects_page.sjs b/dom/security/test/csp/file_redirects_page.sjs
new file mode 100644
index 000000000..ced2d0787
--- /dev/null
+++ b/dom/security/test/csp/file_redirects_page.sjs
@@ -0,0 +1,103 @@
+// SJS file for CSP redirect mochitests
+// This file serves pages which can optionally specify a Content Security Policy
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs";
+
+ // CSP header value
+ response.setHeader("Content-Security-Policy",
+ "default-src 'self' blob: ; style-src 'self' 'unsafe-inline'", false);
+
+ // downloadable font that redirects to another site
+ if (query["testid"] == "font-src") {
+ var resp = '<style type="text/css"> @font-face { font-family:' +
+ '"Redirecting Font"; src: url("' + resource +
+ '?res=font&redir=other&id=font-src-redir")} #test{font-family:' +
+ '"Redirecting Font"}</style></head><body>' +
+ '<div id="test">test</div></body>';
+ response.write(resp);
+ return;
+ }
+
+ // iframe that redirects to another site
+ if (query["testid"] == "frame-src") {
+ response.write('<iframe src="'+resource+'?res=iframe&redir=other&id=frame-src-redir"></iframe>');
+ return;
+ }
+
+ // image that redirects to another site
+ if (query["testid"] == "img-src") {
+ response.write('<img src="'+resource+'?res=image&redir=other&id=img-src-redir" />');
+ return;
+ }
+
+ // video content that redirects to another site
+ if (query["testid"] == "media-src") {
+ response.write('<video src="'+resource+'?res=media&redir=other&id=media-src-redir"></video>');
+ return;
+ }
+
+ // object content that redirects to another site
+ if (query["testid"] == "object-src") {
+ response.write('<object type="text/html" data="'+resource+'?res=object&redir=other&id=object-src-redir"></object>');
+ return;
+ }
+
+ // external script that redirects to another site
+ if (query["testid"] == "script-src") {
+ response.write('<script src="'+resource+'?res=script&redir=other&id=script-src-redir"></script>');
+ return;
+ }
+
+ // external stylesheet that redirects to another site
+ if (query["testid"] == "style-src") {
+ response.write('<link rel="stylesheet" type="text/css" href="'+resource+'?res=style&redir=other&id=style-src-redir"></link>');
+ return;
+ }
+
+ // script that XHR's to a resource that redirects to another site
+ if (query["testid"] == "xhr-src") {
+ response.write('<script src="'+resource+'?res=xhr"></script>');
+ return;
+ }
+
+ // for bug949706
+ if (query["testid"] == "img-src-from-css") {
+ // loads a stylesheet, which in turn loads an image that redirects.
+ response.write('<link rel="stylesheet" type="text/css" href="'+resource+'?res=cssLoader&id=img-src-redir-from-css">');
+ return;
+ }
+
+ if (query["testid"] == "from-worker") {
+ // loads a script; launches a worker; that worker uses importscript; which then gets redirected
+ // So it's:
+ // <script src="res=loadWorkerThatMakesRequests">
+ // .. loads Worker("res=makeRequestsWorker")
+ // .. calls importScript("res=script")
+ // .. calls xhr("res=xhr-resp")
+ // .. calls fetch("res=xhr-resp")
+ response.write('<script src="'+resource+'?res=loadWorkerThatMakesRequests&id=from-worker"></script>');
+ return;
+ }
+
+ if (query["testid"] == "from-blob-worker") {
+ // loads a script; launches a worker; that worker uses importscript; which then gets redirected
+ // So it's:
+ // <script src="res=loadBlobWorkerThatMakesRequests">
+ // .. loads Worker("res=makeRequestsWorker")
+ // .. calls importScript("res=script")
+ // .. calls xhr("res=xhr-resp")
+ // .. calls fetch("res=xhr-resp")
+ response.write('<script src="'+resource+'?res=loadBlobWorkerThatMakesRequests&id=from-blob-worker"></script>');
+ return;
+ }
+}
diff --git a/dom/security/test/csp/file_redirects_resource.sjs b/dom/security/test/csp/file_redirects_resource.sjs
new file mode 100644
index 000000000..da138630f
--- /dev/null
+++ b/dom/security/test/csp/file_redirects_resource.sjs
@@ -0,0 +1,149 @@
+// SJS file to serve resources for CSP redirect tests
+// This file mimics serving resources, e.g. fonts, images, etc., which a CSP
+// can include. The resource may redirect to a different resource, if specified.
+function handleRequest(request, response)
+{
+ var query = {};
+ request.queryString.split('&').forEach(function (val) {
+ var [name, value] = val.split('=');
+ query[name] = unescape(value);
+ });
+
+ var thisSite = "http://mochi.test:8888";
+ var otherSite = "http://example.com";
+ var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs";
+
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // redirect to a resource on this site
+ if (query["redir"] == "same") {
+ var loc = thisSite+resource+"?res="+query["res"]+"&testid="+query["id"];
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+ }
+
+ // redirect to a resource on a different site
+ else if (query["redir"] == "other") {
+ var loc = otherSite+resource+"?res="+query["res"]+"&testid="+query["id"];
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", loc, false);
+ return;
+ }
+
+ // not a redirect. serve some content.
+ // the content doesn't have to be valid, since we're only checking whether
+ // the request for the content was sent or not.
+
+ // downloadable font
+ if (query["res"] == "font") {
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("font data...");
+ return;
+ }
+
+ // iframe with arbitrary content
+ if (query["res"] == "iframe") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("iframe content...");
+ return;
+ }
+
+ // image
+ if (query["res"] == "image") {
+ response.setHeader("Content-Type", "image/gif", false);
+ response.write("image data...");
+ return;
+ }
+
+ // media content, e.g. Ogg video
+ if (query["res"] == "media") {
+ response.setHeader("Content-Type", "video/ogg", false);
+ response.write("video data...");
+ return;
+ }
+
+ // plugin content, e.g. <object>
+ if (query["res"] == "object") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("object data...");
+ return;
+ }
+
+ // script
+ if (query["res"] == "script") {
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write("some script...");
+ return;
+ }
+
+ // external stylesheet
+ if (query["res"] == "style") {
+ response.setHeader("Content-Type", "text/css", false);
+ response.write("css data...");
+ return;
+ }
+
+ // internal stylesheet that loads an image from an external site
+ if (query["res"] == "cssLoader") {
+ let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"];
+ response.setHeader("Content-Type", "text/css", false);
+ response.write("body { background:url('" + bgURL + "'); }");
+ return;
+ }
+
+ // script that loads an internal worker that uses importScripts on a redirect
+ // to an external script.
+ if (query["res"] == "loadWorkerThatMakesRequests") {
+ // this creates a worker (same origin) that imports a redirecting script.
+ let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"];
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write("new Worker('" + workerURL + "');");
+ return;
+ }
+
+ // script that loads an internal worker that uses importScripts on a redirect
+ // to an external script.
+ if (query["res"] == "loadBlobWorkerThatMakesRequests") {
+ // this creates a worker (same origin) that imports a redirecting script.
+ let workerURL = thisSite + resource + '?res=makeRequestsWorker&id=' + query["id"];
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write("var x = new XMLHttpRequest(); x.open('GET', '" + workerURL + "'); ");
+ response.write("x.responseType = 'blob'; x.send(); ");
+ response.write("x.onload = () => { new Worker(URL.createObjectURL(x.response)); };");
+ return;
+ }
+
+ // source for a worker that simply calls importScripts on a script that
+ // redirects.
+ if (query["res"] == "makeRequestsWorker") {
+ // this is code for a worker that imports a redirected script.
+ let scriptURL = thisSite + resource + "?redir=other&res=script&id=script-src-redir-" + query["id"];
+ let xhrURL = thisSite + resource + "?redir=other&res=xhr-resp&id=xhr-src-redir-" + query["id"];
+ let fetchURL = thisSite + resource + "?redir=other&res=xhr-resp&id=fetch-src-redir-" + query["id"];
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.write("try { importScripts('" + scriptURL + "'); } catch(ex) {} ");
+ response.write("var x = new XMLHttpRequest(); x.open('GET', '" + xhrURL + "'); x.send();");
+ response.write("fetch('" + fetchURL + "');");
+ return;
+ }
+
+ // script that invokes XHR
+ if (query["res"] == "xhr") {
+ response.setHeader("Content-Type", "application/javascript", false);
+ var resp = 'var x = new XMLHttpRequest();x.open("GET", "' + thisSite +
+ resource+'?redir=other&res=xhr-resp&id=xhr-src-redir", false);\n' +
+ 'x.send(null);';
+ response.write(resp);
+ return;
+ }
+
+ // response to XHR
+ if (query["res"] == "xhr-resp") {
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.setHeader("Content-Type", "text/html", false);
+ response.write('XHR response...');
+ return;
+ }
+}
diff --git a/dom/security/test/csp/file_referrerdirective.html b/dom/security/test/csp/file_referrerdirective.html
new file mode 100644
index 000000000..841ffe058
--- /dev/null
+++ b/dom/security/test/csp/file_referrerdirective.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Subframe test for bug 965727</title>
+
+<script type="text/javascript">
+// we can get the ID out of the querystring.
+var args = document.location.search.substring(1).split('&');
+var id = "unknown";
+for (var i=0; i < args.length; i++) {
+ var arg = unescape(args[i]);
+ if (arg.indexOf('=') > 0 && arg.indexOf('id') == 0) {
+ id = arg.split('=')[1].trim();
+ }
+}
+
+var results = {
+ 'id': id,
+ 'referrer': document.location.href,
+ 'results': {
+ 'sameorigin': false,
+ 'crossorigin': false,
+ 'downgrade': false
+ }
+};
+
+// this is called back by each script load.
+var postResult = function(loadType, referrerLevel, referrer) {
+ results.results[loadType] = referrerLevel;
+
+ // and then check if all three have loaded.
+ for (var id in results.results) {
+ if (!results.results[id]) {
+ return;
+ }
+ }
+ //finished if we don't return early
+ window.parent.postMessage(JSON.stringify(results), "*");
+ console.log(JSON.stringify(results));
+}
+
+</script>
+</head>
+<body>
+Testing ...
+
+<script src="https://example.com/tests/dom/security/test/csp/referrerdirective.sjs?type=sameorigin&"
+ onerror="postResult('sameorigin', 'error');"></script>
+<script src="https://test2.example.com/tests/dom/security/test/csp/referrerdirective.sjs?type=crossorigin&"
+ onerror="postResult('crossorigin', 'error');"></script>
+<script src="http://example.com/tests/dom/security/test/csp/referrerdirective.sjs?type=downgrade&"
+ onerror="postResult('downgrade', 'error');"></script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_report.html b/dom/security/test/csp/file_report.html
new file mode 100644
index 000000000..fb18af805
--- /dev/null
+++ b/dom/security/test/csp/file_report.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1033424 - Test csp-report properties </title>
+ </head>
+ <body>
+ <script type="text/javascript">
+ var foo = "propEscFoo";
+ var bar = "propEscBar";
+ // just verifying that we properly escape newlines and quotes
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_report_chromescript.js b/dom/security/test/csp/file_report_chromescript.js
new file mode 100644
index 000000000..bf4f70edb
--- /dev/null
+++ b/dom/security/test/csp/file_report_chromescript.js
@@ -0,0 +1,54 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+
+const reportURI = "http://mochi.test:8888/foo.sjs";
+
+var openingObserver = {
+ observe: function(subject, topic, data) {
+ // subject should be an nsURI
+ if (subject.QueryInterface == undefined)
+ return;
+
+ var message = {report: "", error: false};
+
+ if (topic == 'http-on-opening-request') {
+ var asciiSpec = subject.QueryInterface(Ci.nsIHttpChannel).URI.asciiSpec;
+ if (asciiSpec !== reportURI) return;
+
+ var reportText = false;
+ try {
+ // Verify that the report was properly formatted.
+ // We'll parse the report text as JSON and verify that the properties
+ // have expected values.
+ var reportText = "{}";
+ var uploadStream = subject.QueryInterface(Ci.nsIUploadChannel).uploadStream;
+
+ if (uploadStream) {
+ // get the bytes from the request body
+ var binstream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
+ binstream.setInputStream(uploadStream);
+
+ var segments = [];
+ for (var count = uploadStream.available(); count; count = uploadStream.available()) {
+ var data = binstream.readBytes(count);
+ segments.push(data);
+ }
+
+ var reportText = segments.join("");
+ // rewind stream as we are supposed to - there will be an assertion later if we don't.
+ uploadStream.QueryInterface(Ci.nsISeekableStream).seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
+ }
+ message.report = reportText;
+ } catch (e) {
+ message.error = e.toString();
+ }
+
+ sendAsyncMessage('opening-request-completed', message);
+ Services.obs.removeObserver(openingObserver, 'http-on-opening-request');
+ }
+ }
+};
+
+Services.obs.addObserver(openingObserver, 'http-on-opening-request', false);
diff --git a/dom/security/test/csp/file_report_for_import.css b/dom/security/test/csp/file_report_for_import.css
new file mode 100644
index 000000000..b578b77b3
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import.css
@@ -0,0 +1 @@
+@import url("http://example.com/tests/dom/security/test/csp/file_report_for_import_server.sjs?stylesheet");
diff --git a/dom/security/test/csp/file_report_for_import.html b/dom/security/test/csp/file_report_for_import.html
new file mode 100644
index 000000000..77a36faea
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1048048 - Test sending csp-report when using import in css</title>
+ <link rel="stylesheet" type="text/css" href="file_report_for_import.css">
+ </head>
+ <body>
+ empty body, just testing @import in the included css for bug 1048048
+</body>
+</html>
diff --git a/dom/security/test/csp/file_report_for_import_server.sjs b/dom/security/test/csp/file_report_for_import_server.sjs
new file mode 100644
index 000000000..e69852b1b
--- /dev/null
+++ b/dom/security/test/csp/file_report_for_import_server.sjs
@@ -0,0 +1,49 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 1048048 - CSP violation report not sent for @import
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream");
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+ var queryString = request.queryString;
+
+ // (1) lets process the queryresult request async and
+ // wait till we have received the image request.
+ if (queryString === "queryresult") {
+ response.processAsync();
+ setObjectState("queryResult", response);
+ return;
+ }
+
+ // (2) handle the csp-report and return the JSON back to
+ // the testfile using the afore stored xml request in (1).
+ if (queryString === "report") {
+ getObjectState("queryResult", function(queryResponse) {
+ if (!queryResponse) {
+ return;
+ }
+
+ // send the report back to the XML request for verification
+ var report = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+ while ((avail = report.available()) > 0) {
+ Array.prototype.push.apply(bytes, report.readByteArray(avail));
+ }
+ var data = String.fromCharCode.apply(null, bytes);
+ queryResponse.bodyOutputStream.write(data, data.length);
+ queryResponse.finish();
+ });
+ return;
+ }
+
+ // we should not get here ever, but just in case return
+ // something unexpected.
+ response.write("doh!");
+}
diff --git a/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html b/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html
diff --git a/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html^headers^ b/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html^headers^
new file mode 100644
index 000000000..3f2fdfe9e
--- /dev/null
+++ b/dom/security/test/csp/file_report_uri_missing_in_report_only_header.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy-Report-Only: default-src 'self';
diff --git a/dom/security/test/csp/file_require_sri_meta.js b/dom/security/test/csp/file_require_sri_meta.js
new file mode 100644
index 000000000..af0113297
--- /dev/null
+++ b/dom/security/test/csp/file_require_sri_meta.js
@@ -0,0 +1 @@
+var foo = 24;
diff --git a/dom/security/test/csp/file_require_sri_meta.sjs b/dom/security/test/csp/file_require_sri_meta.sjs
new file mode 100644
index 000000000..acaf742db
--- /dev/null
+++ b/dom/security/test/csp/file_require_sri_meta.sjs
@@ -0,0 +1,54 @@
+// custom *.sjs for Bug 1277557
+// META CSP: require-sri-for script;
+
+const PRE_INTEGRITY =
+ "<!DOCTYPE HTML>" +
+ "<html><head><meta charset=\"utf-8\">" +
+ "<title>Bug 1277557 - CSP require-sri-for does not block when CSP is in meta tag</title>" +
+ "<meta http-equiv=\"Content-Security-Policy\" content=\"require-sri-for script; script-src 'unsafe-inline' *\">" +
+ "</head>" +
+ "<body>" +
+ "<script id=\"testscript\"" +
+ // Using math.random() to avoid confusing cache behaviors within the test
+ " src=\"http://mochi.test:8888/tests/dom/security/test/csp/file_require_sri_meta.js?" + Math.random() + "\"";
+
+const WRONG_INTEGRITY =
+ " integrity=\"sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC\"";
+
+const CORRECT_INEGRITY =
+ " integrity=\"sha384-PkcuZQHmjBQKRyv1v3x0X8qFmXiSyFyYIP+f9SU86XWvRneifdNCPg2cYFWBuKsF\"";
+
+const POST_INTEGRITY =
+ " onload=\"window.parent.postMessage({result: 'script-loaded'}, '*');\"" +
+ " onerror=\"window.parent.postMessage({result: 'script-blocked'}, '*');\"" +
+ "></script>" +
+ "</body>" +
+ "</html>";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var queryString = request.queryString;
+
+ if (queryString === "no-sri") {
+ response.write(PRE_INTEGRITY + POST_INTEGRITY);
+ return;
+ }
+
+ if (queryString === "wrong-sri") {
+ response.write(PRE_INTEGRITY + WRONG_INTEGRITY + POST_INTEGRITY);
+ return;
+ }
+
+ if (queryString === "correct-sri") {
+ response.write(PRE_INTEGRITY + CORRECT_INEGRITY + POST_INTEGRITY);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_sandbox_1.html b/dom/security/test/csp/file_sandbox_1.html
new file mode 100644
index 000000000..ce1e80c86
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_1.html
@@ -0,0 +1,16 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox="allow-same-origin" -->
+ <!-- Content-Security-Policy: default-src 'self' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img1_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img1a_good&type=img/png" />
+ <!-- should not execute script -->
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_10.html b/dom/security/test/csp/file_sandbox_10.html
new file mode 100644
index 000000000..f934497ee
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_10.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- Content-Security-Policy: default-src 'none'; sandbox -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img10_bad&type=img/png"> </img>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img10a_bad&type=img/png" />
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_11.html b/dom/security/test/csp/file_sandbox_11.html
new file mode 100644
index 000000000..03697438a
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_11.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+ }
+</script>
+<script src='file_sandbox_fail.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with only inline "allow-scripts"
+
+ <!-- Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img11_bad&type=img/png" />
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img11a_bad&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script11_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script11a_bad&type=text/javascript'></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_12.html b/dom/security/test/csp/file_sandbox_12.html
new file mode 100644
index 000000000..8bed9dc59
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_12.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+
+ document.getElementById('a_form').submit();
+
+ // trigger the javascript: url test
+ sendMouseEvent({type:'click'}, 'a_link');
+ }
+</script>
+<script src='file_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with "allow-same-origin" and allow-scripts"
+
+
+ <!-- Content-Security-Policy: sandbox allow-same-origin allow-scripts; default-src 'self' 'unsafe-inline'; -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img12_bad&type=img/png"> </img>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script12_bad&type=text/javascript'></script>
+
+ <form method="get" action="/tests/content/html/content/test/file_iframe_sandbox_form_fail.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" onclick="doSubmit()" id="a_button">
+ </form>
+
+ <a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_13.html b/dom/security/test/csp/file_sandbox_13.html
new file mode 100644
index 000000000..e4672ed05
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_13.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+ }
+</script>
+<script src='file_sandbox_fail.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with only inline "allow-scripts"
+
+ <!-- Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img13_bad&type=img/png" />
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img13a_bad&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script13_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script13a_bad&type=text/javascript'></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_2.html b/dom/security/test/csp/file_sandbox_2.html
new file mode 100644
index 000000000..b37aa1bce
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_2.html
@@ -0,0 +1,16 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox -->
+ <!-- Content-Security-Policy: default-src 'self' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
+ <!-- should not execute script -->
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_3.html b/dom/security/test/csp/file_sandbox_3.html
new file mode 100644
index 000000000..ba808e47d
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_3.html
@@ -0,0 +1,13 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox="allow-same-origin" -->
+ <!-- Content-Security-Policy: default-src 'none' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_4.html b/dom/security/test/csp/file_sandbox_4.html
new file mode 100644
index 000000000..b2d4ed094
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_4.html
@@ -0,0 +1,13 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- sandbox -->
+ <!-- Content-Security-Policy: default-src 'none' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/base/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
+ <img src="/tests/dom/base/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
+ <script src='/tests/dom/base/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_5.html b/dom/security/test/csp/file_sandbox_5.html
new file mode 100644
index 000000000..79894eabb
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_5.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head> <meta charset="utf-8"> </head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+ }
+</script>
+<script src='file_sandbox_fail.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with only inline "allow-scripts"
+
+ <!-- sandbox="allow-scripts" -->
+ <!-- Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
+ <script src='/tests/dom/security/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_6.html b/dom/security/test/csp/file_sandbox_6.html
new file mode 100644
index 000000000..c23d87860
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_6.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+
+ document.getElementById('a_form').submit();
+
+ // trigger the javascript: url test
+ sendMouseEvent({type:'click'}, 'a_link');
+ }
+</script>
+<script src='file_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with "allow-same-origin" and allow-scripts"
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
+ <script src='http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
+
+ <form method="get" action="/tests/content/html/content/test/file_iframe_sandbox_form_fail.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" onclick="doSubmit()" id="a_button">
+ </form>
+
+ <a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_7.html b/dom/security/test/csp/file_sandbox_7.html
new file mode 100644
index 000000000..3b249d410
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_7.html
@@ -0,0 +1,15 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- Content-Security-Policy: default-src 'self'; sandbox allow-same-origin -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img7_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img7a_good&type=img/png" />
+ <!-- should not execute script -->
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_8.html b/dom/security/test/csp/file_sandbox_8.html
new file mode 100644
index 000000000..4f9cd8916
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_8.html
@@ -0,0 +1,15 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- Content-Security-Policy: sandbox; default-src 'self' -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img8_bad&type=img/png"> </img>
+
+ <!-- these should load ok -->
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img8a_good&type=img/png" />
+ <!-- should not execute script -->
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_9.html b/dom/security/test/csp/file_sandbox_9.html
new file mode 100644
index 000000000..29ffc191c
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_9.html
@@ -0,0 +1,12 @@
+<html>
+<head> <meta charset="utf-8"> </head>
+ <body>
+ <!-- Content-Security-Policy: default-src 'none'; sandbox allow-same-origin -->
+
+ <!-- these should be stopped by CSP -->
+ <img src="http://example.org/tests/dom/security/test/csp/file_CSP.sjs?testid=img9_bad&type=img/png"> </img>
+ <img src="/tests/dom/security/test/csp/file_CSP.sjs?testid=img9a_bad&type=img/png" />
+
+ <script src='/tests/dom/security/test/csp/file_sandbox_fail.js'></script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_allow_scripts.html b/dom/security/test/csp/file_sandbox_allow_scripts.html
new file mode 100644
index 000000000..faab9f0fc
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_allow_scripts.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Bug 1396320: Fix CSP sandbox regression for allow-scripts</title>
+ </head>
+<body>
+<script type='application/javascript'>
+ window.parent.postMessage({result: document.domain }, '*');
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_sandbox_allow_scripts.html^headers^ b/dom/security/test/csp/file_sandbox_allow_scripts.html^headers^
new file mode 100644
index 000000000..4705ce9de
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_allow_scripts.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts;
diff --git a/dom/security/test/csp/file_sandbox_fail.js b/dom/security/test/csp/file_sandbox_fail.js
new file mode 100644
index 000000000..5403ff218
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_fail.js
@@ -0,0 +1,4 @@
+function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+}
+ok(false, "documents sandboxed with allow-scripts should NOT be able to run <script src=...>");
diff --git a/dom/security/test/csp/file_sandbox_pass.js b/dom/security/test/csp/file_sandbox_pass.js
new file mode 100644
index 000000000..2fd0350dc
--- /dev/null
+++ b/dom/security/test/csp/file_sandbox_pass.js
@@ -0,0 +1,4 @@
+function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc: desc}, "*");
+}
+ok(true, "documents sandboxed with allow-scripts should be able to run <script src=...>");
diff --git a/dom/security/test/csp/file_scheme_relative_sources.js b/dom/security/test/csp/file_scheme_relative_sources.js
new file mode 100644
index 000000000..09286d42e
--- /dev/null
+++ b/dom/security/test/csp/file_scheme_relative_sources.js
@@ -0,0 +1 @@
+document.getElementById("testdiv").innerHTML = "allowed";
diff --git a/dom/security/test/csp/file_scheme_relative_sources.sjs b/dom/security/test/csp/file_scheme_relative_sources.sjs
new file mode 100644
index 000000000..8c4d62ca5
--- /dev/null
+++ b/dom/security/test/csp/file_scheme_relative_sources.sjs
@@ -0,0 +1,42 @@
+/**
+ * Custom *.sjs specifically for the needs of
+ * Bug 921493 - CSP: test whitelisting of scheme-relative sources
+ */
+
+function handleRequest(request, response)
+{
+ Components.utils.importGlobalProperties(["URLSearchParams"]);
+ let query = new URLSearchParams(request.queryString);
+
+ let scheme = query.get("scheme");
+ let policy = query.get("policy");
+
+ let linkUrl = scheme +
+ "://example.com/tests/dom/security/test/csp/file_scheme_relative_sources.js";
+
+ let html = "<!DOCTYPE HTML>" +
+ "<html>" +
+ "<head>" +
+ "<title>test schemeless sources within CSP</title>" +
+ "</head>" +
+ "<body> " +
+ "<div id='testdiv'>blocked</div>" +
+ // try to load a scheme relative script
+ "<script src='" + linkUrl + "'></script>" +
+ // have an inline script that reports back to the parent whether
+ // the script got loaded or not from within the sandboxed iframe.
+ "<script type='application/javascript'>" +
+ "window.onload = function() {" +
+ "var inner = document.getElementById('testdiv').innerHTML;" +
+ "window.parent.postMessage({ result: inner }, '*');" +
+ "}" +
+ "</script>" +
+ "</body>" +
+ "</html>";
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Security-Policy", policy, false);
+
+ response.write(html);
+}
diff --git a/dom/security/test/csp/file_self_none_as_hostname_confusion.html b/dom/security/test/csp/file_self_none_as_hostname_confusion.html
new file mode 100644
index 000000000..16196bb19
--- /dev/null
+++ b/dom/security/test/csp/file_self_none_as_hostname_confusion.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 587377 - CSP keywords "'self'" and "'none'" are easy to confuse with host names "self" and "none"</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_self_none_as_hostname_confusion.html^headers^ b/dom/security/test/csp/file_self_none_as_hostname_confusion.html^headers^
new file mode 100644
index 000000000..26af7ed9b
--- /dev/null
+++ b/dom/security/test/csp/file_self_none_as_hostname_confusion.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self' SELF;
diff --git a/dom/security/test/csp/file_sendbeacon.html b/dom/security/test/csp/file_sendbeacon.html
new file mode 100644
index 000000000..13202c65f
--- /dev/null
+++ b/dom/security/test/csp/file_sendbeacon.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="Content-Security-Policy" content= "connect-src 'none'">
+ <title>Bug 1234813 - sendBeacon should not throw if blocked by Content Policy</title>
+</head>
+<body>
+
+<script type="application/javascript">
+try {
+ navigator.sendBeacon("http://example.com/sendbeaconintonirvana");
+ window.parent.postMessage({result: "blocked-beacon-does-not-throw"}, "*");
+ }
+ catch (e) {
+ window.parent.postMessage({result: "blocked-beacon-throws"}, "*");
+ }
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_service_worker.html b/dom/security/test/csp/file_service_worker.html
new file mode 100644
index 000000000..00a2b4020
--- /dev/null
+++ b/dom/security/test/csp/file_service_worker.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1208559 - ServiceWorker registration not governed by CSP</title>
+</head>
+<body>
+<script>
+ function finish(status) {
+ window.parent.postMessage({result: status}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(finish.bind(null, 'allowed'),
+ finish.bind(null, 'blocked'));
+ navigator.serviceWorker
+ .register("file_service_worker.js", {scope: "."})
+ .then(null, finish.bind(null, 'blocked'));
+ </script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_service_worker.js b/dom/security/test/csp/file_service_worker.js
new file mode 100644
index 000000000..1bf583f4c
--- /dev/null
+++ b/dom/security/test/csp/file_service_worker.js
@@ -0,0 +1 @@
+dump("service workers: hello world");
diff --git a/dom/security/test/csp/file_shouldprocess.html b/dom/security/test/csp/file_shouldprocess.html
new file mode 100644
index 000000000..0684507e9
--- /dev/null
+++ b/dom/security/test/csp/file_shouldprocess.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Helper for Test Bug 908933</title>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <object type="application/x-java-test" codebase="test1"></object>
+
+ <object classid="java:test2" codebase="./test2"></object>
+
+ <object data="test3" classid="java:test3" codebase="./test3"></object>
+
+ <applet codebase="test4"></applet>
+
+ <embed src="test5.class" codebase="test5" type="application/x-java-test">
+
+ <embed type="application/x-java-test" codebase="test6">
+
+ <embed src="test7.class">
+
+ <embed src="test8.class" codebase="test8">
+
+ </body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic.js b/dom/security/test/csp/file_strict_dynamic.js
new file mode 100644
index 000000000..09286d42e
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic.js
@@ -0,0 +1 @@
+document.getElementById("testdiv").innerHTML = "allowed";
diff --git a/dom/security/test/csp/file_strict_dynamic_default_src.html b/dom/security/test/csp/file_strict_dynamic_default_src.html
new file mode 100644
index 000000000..35feacb4f
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_default_src.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+
+<div id="testdiv">blocked</div>
+<script nonce="foo" src="http://mochi.test:8888/tests/dom/security/test/csp/file_strict_dynamic_default_src.js"></script>
+
+<img id="testimage" src="http://mochi.test:8888/tests/image/test/mochitest/blue.png"></img>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_default_src.js b/dom/security/test/csp/file_strict_dynamic_default_src.js
new file mode 100644
index 000000000..09286d42e
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_default_src.js
@@ -0,0 +1 @@
+document.getElementById("testdiv").innerHTML = "allowed";
diff --git a/dom/security/test/csp/file_strict_dynamic_js_url.html b/dom/security/test/csp/file_strict_dynamic_js_url.html
new file mode 100644
index 000000000..bd53b0adb
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_js_url.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1316826 - 'strict-dynamic' blocking DOM event handlers</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<a id="jslink" href='javascript:document.getElementById("testdiv").innerHTML = "allowed"'>click me</a>
+<script nonce="foo">
+ document.getElementById("jslink").click();
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_non_parser_inserted.html b/dom/security/test/csp/file_strict_dynamic_non_parser_inserted.html
new file mode 100644
index 000000000..c51fefd72
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_non_parser_inserted.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ // generates a *non* parser inserted script and should be allowed
+ var myScript = document.createElement('script');
+ myScript.src = 'http://example.com/tests/dom/security/test/csp/file_strict_dynamic.js';
+ document.head.appendChild(myScript);
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_non_parser_inserted_inline.html b/dom/security/test/csp/file_strict_dynamic_non_parser_inserted_inline.html
new file mode 100644
index 000000000..10a0f32e4
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_non_parser_inserted_inline.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ var dynamicScript = document.createElement('script');
+ dynamicScript.textContent = 'document.getElementById("testdiv").textContent="allowed"';
+ document.head.appendChild(dynamicScript);
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write.html b/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write.html
new file mode 100644
index 000000000..2a3a7d499
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ // generates a parser inserted script and should be blocked
+ document.write("<script src='http://example.com/tests/dom/security/test/csp/file_strict_dynamic.js'><\/script>");
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html b/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html
new file mode 100644
index 000000000..9938ef2dc
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ // generates a parser inserted script with a valid nonce- and should be allowed
+ document.write("<script nonce='foo' src='http://example.com/tests/dom/security/test/csp/file_strict_dynamic.js'><\/script>");
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_script_events.html b/dom/security/test/csp/file_strict_dynamic_script_events.html
new file mode 100644
index 000000000..088958382
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_script_events.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1316826 - 'strict-dynamic' blocking DOM event handlers</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+ <img src='/nonexisting.jpg'
+ onerror='document.getElementById("testdiv").innerHTML = "allowed";'
+ style='display:none'>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_script_events_xbl.html b/dom/security/test/csp/file_strict_dynamic_script_events_xbl.html
new file mode 100644
index 000000000..701ef3226
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_script_events_xbl.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1316826 - 'strict-dynamic' blocking DOM event handlers</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<marquee onstart='document.getElementById("testdiv").innerHTML = "allowed";'>
+ Bug 1316826
+</marquee>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_script_extern.html b/dom/security/test/csp/file_strict_dynamic_script_extern.html
new file mode 100644
index 000000000..94b6aefb1
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_script_extern.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+<script nonce="foo" src="http://example.com/tests/dom/security/test/csp/file_strict_dynamic.js"></script>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_script_inline.html b/dom/security/test/csp/file_strict_dynamic_script_inline.html
new file mode 100644
index 000000000..d17a58f27
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_script_inline.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ document.getElementById("testdiv").innerHTML = "allowed";
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_strict_dynamic_unsafe_eval.html b/dom/security/test/csp/file_strict_dynamic_unsafe_eval.html
new file mode 100644
index 000000000..f0b26da91
--- /dev/null
+++ b/dom/security/test/csp/file_strict_dynamic_unsafe_eval.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+</head>
+<body>
+<div id="testdiv">blocked</div>
+
+<script nonce="foo">
+ eval('document.getElementById("testdiv").innerHTML = "allowed";');
+</script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/dom/security/test/csp/file_subframe_run_js_if_allowed.html b/dom/security/test/csp/file_subframe_run_js_if_allowed.html
new file mode 100644
index 000000000..3ba970ce8
--- /dev/null
+++ b/dom/security/test/csp/file_subframe_run_js_if_allowed.html
@@ -0,0 +1,13 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=702439
+
+This document is a child frame of a CSP document and the
+test verifies that it is permitted to run javascript: URLs
+if the parent has a policy that allows them.
+-->
+<body onload="document.getElementById('a').click()">
+<a id="a" href="javascript:parent.javascript_link_ran = true;
+ parent.checkResult();">click</a>
+</body>
+</html>
diff --git a/dom/security/test/csp/file_subframe_run_js_if_allowed.html^headers^ b/dom/security/test/csp/file_subframe_run_js_if_allowed.html^headers^
new file mode 100644
index 000000000..233b35931
--- /dev/null
+++ b/dom/security/test/csp/file_subframe_run_js_if_allowed.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src *; script-src 'unsafe-inline'
diff --git a/dom/security/test/csp/file_testserver.sjs b/dom/security/test/csp/file_testserver.sjs
new file mode 100644
index 000000000..ae1826611
--- /dev/null
+++ b/dom/security/test/csp/file_testserver.sjs
@@ -0,0 +1,57 @@
+// SJS file for CSP mochitests
+"use strict";
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function loadHTMLFromFile(path) {
+ // Load the HTML to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ const testHTMLFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+
+ const testHTMLFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+
+ path
+ .split("/")
+ .filter(path => path)
+ .reduce((file, path) => {
+ testHTMLFile.append(path)
+ return testHTMLFile;
+ }, testHTMLFile);
+ testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+ const isAvailable = testHTMLFileStream.available();
+ return NetUtil.readInputStreamToString(testHTMLFileStream, isAvailable);
+}
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // Deliver the CSP policy encoded in the URL
+ if(query.has("csp")){
+ response.setHeader("Content-Security-Policy", query.get("csp"), false);
+ }
+
+ // Deliver the CSP report-only policy encoded in the URI
+ if(query.has("cspRO")){
+ response.setHeader("Content-Security-Policy-Report-Only", query.get("cspRO"), false);
+ }
+
+ // Deliver the CORS header in the URL
+ if(query.has("cors")){
+ response.setHeader("Access-Control-Allow-Origin", query.get("cors"), false);
+ }
+
+ // Send HTML to test allowed/blocked behaviors
+ response.setHeader("Content-Type", "text/html", false);
+ if(query.has("file")){
+ response.write(loadHTMLFromFile(query.get("file")));
+ }
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure.html b/dom/security/test/csp/file_upgrade_insecure.html
new file mode 100644
index 000000000..d104a3a24
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- style -->
+ <link rel='stylesheet' type='text/css' href='http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?style' media='screen' />
+
+ <!-- font -->
+ <style>
+ @font-face {
+ font-family: "foofont";
+ src: url('http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?font');
+ }
+ .div_foo { font-family: "foofont"; }
+ </style>
+</head>
+<body>
+
+ <!-- images: -->
+ <img src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?img"></img>
+
+ <!-- redirects: upgrade http:// to https:// redirect to http:// and then upgrade to https:// again -->
+ <img src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?redirect-image"></img>
+
+ <!-- script: -->
+ <script src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?script"></script>
+
+ <!-- media: -->
+ <audio src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?media"></audio>
+
+ <!-- objects: -->
+ <object width="10" height="10" data="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?object"></object>
+
+ <!-- font: (apply font loaded in header to div) -->
+ <div class="div_foo">foo</div>
+
+ <!-- iframe: (same origin) -->
+ <iframe src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?iframe">
+ <!-- within that iframe we load an image over http and make sure the requested gets upgraded to https -->
+ </iframe>
+
+ <!-- xhr: -->
+ <script type="application/javascript">
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?xhr");
+ myXHR.send(null);
+ </script>
+
+ <!-- websockets: upgrade ws:// to wss://-->
+ <script type="application/javascript">
+ var mySocket = new WebSocket("ws://example.com/tests/dom/security/test/csp/file_upgrade_insecure");
+ mySocket.onopen = function(e) {
+ if (mySocket.url.includes("wss://")) {
+ window.parent.postMessage({result: "websocket-ok"}, "*");
+ }
+ else {
+ window.parent.postMessage({result: "websocket-error"}, "*");
+ }
+ };
+ mySocket.onerror = function(e) {
+ window.parent.postMessage({result: "websocket-unexpected-error"}, "*");
+ };
+ </script>
+
+ <!-- form action: (upgrade POST from http:// to https://) -->
+ <iframe name='formFrame' id='formFrame'></iframe>
+ <form target="formFrame" action="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?form" method="POST">
+ <input name="foo" value="foo">
+ <input type="submit" id="submitButton" formenctype='multipart/form-data' value="Submit form">
+ </form>
+ <script type="text/javascript">
+ var submitButton = document.getElementById('submitButton');
+ submitButton.click();
+ </script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_upgrade_insecure_cors.html b/dom/security/test/csp/file_upgrade_insecure_cors.html
new file mode 100644
index 000000000..e675c62e9
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_cors.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+</head>
+<body>
+
+<script type="text/javascript">
+ // === TEST 1
+ var url1 = "http://test1.example.com/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?test1";
+ var xhr1 = new XMLHttpRequest();
+ xhr1.open("GET", url1, true);
+ xhr1.onload = function() {
+ window.parent.postMessage(xhr1.response, "*");
+ };
+ xhr1.onerror = function() {
+ window.parent.postMessage("test1-failed", "*");
+ };
+ xhr1.send();
+
+ // === TEST 2
+ var url2 = "http://test1.example.com/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?test2";
+ var xhr2 = new XMLHttpRequest();
+ xhr2.open("GET", url2, true);
+ xhr2.onload = function() {
+ window.parent.postMessage(xhr2.response, "*");
+ };
+ xhr2.onerror = function() {
+ window.parent.postMessage("test2-failed", "*");
+ };
+ xhr2.send();
+
+ // === TEST 3
+ var url3 = "http://test2.example.com/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?test3";
+ var xhr3 = new XMLHttpRequest();
+ xhr3.open("GET", url3, true);
+ xhr3.onload = function() {
+ window.parent.postMessage(xhr3.response, "*");
+ };
+ xhr3.onerror = function() {
+ window.parent.postMessage("test3-failed", "*");
+ };
+ xhr3.send();
+
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs b/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs
new file mode 100644
index 000000000..33f6c3b23
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs
@@ -0,0 +1,62 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 1139297 - Implement CSP upgrade-insecure-requests directive
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // perform sanity check and make sure that all requests get upgraded to use https
+ if (request.scheme !== "https") {
+ response.write("request not https");
+ return;
+ }
+
+ var queryString = request.queryString;
+
+ // TEST 1
+ if (queryString === "test1") {
+ var newLocation =
+ "http://test1.example.com/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?redir-test1";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", newLocation, false);
+ return;
+ }
+ if (queryString === "redir-test1") {
+ response.write("test1-no-cors-ok");
+ return;
+ }
+
+ // TEST 2
+ if (queryString === "test2") {
+ var newLocation =
+ "http://test1.example.com:443/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?redir-test2";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", newLocation, false);
+ return;
+ }
+ if (queryString === "redir-test2") {
+ response.write("test2-no-cors-diffport-ok");
+ return;
+ }
+
+ // TEST 3
+ response.setHeader("Access-Control-Allow-Headers", "content-type", false);
+ response.setHeader("Access-Control-Allow-Methods", "POST, GET", false);
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+
+ if (queryString === "test3") {
+ var newLocation =
+ "http://test1.example.com/tests/dom/security/test/csp/file_upgrade_insecure_cors_server.sjs?redir-test3";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", newLocation, false);
+ return;
+ }
+ if (queryString === "redir-test3") {
+ response.write("test3-cors-ok");
+ return;
+ }
+
+ // we should not get here, but just in case return something unexpected
+ response.write("d'oh");
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs b/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs
new file mode 100644
index 000000000..6870a57bb
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs
@@ -0,0 +1,54 @@
+// custom *.sjs for Bug 1273430
+// META CSP: upgrade-insecure-requests
+
+// important: the IFRAME_URL is *http* and needs to be upgraded to *https* by upgrade-insecure-requests
+const IFRAME_URL =
+ "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs?docwriteframe";
+
+const TEST_FRAME = `
+ <!DOCTYPE HTML>
+ <html><head><meta charset="utf-8">
+ <title>TEST_FRAME</title>
+ <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+ </head>
+ <body>
+ <script type="text/javascript">
+ document.write('<iframe src="` + IFRAME_URL + `"/>');
+ </script>
+ </body>
+ </html>`;
+
+
+// doc.write(iframe) sends a post message to the parent indicating the current
+// location so the parent can make sure the request was upgraded to *https*.
+const DOC_WRITE_FRAME = `
+ <!DOCTYPE HTML>
+ <html><head><meta charset="utf-8">
+ <title>DOC_WRITE_FRAME</title>
+ </head>
+ <body onload="window.parent.parent.postMessage({result: document.location.href}, '*');">
+ </body>
+ </html>`;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var queryString = request.queryString;
+
+ if (queryString === "testframe") {
+ response.write(TEST_FRAME);
+ return;
+ }
+
+ if (queryString === "docwriteframe") {
+ response.write(DOC_WRITE_FRAME);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_meta.html b/dom/security/test/csp/file_upgrade_insecure_meta.html
new file mode 100644
index 000000000..5f65e78ec
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_meta.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests; default-src https: wss: 'unsafe-inline'; form-action https:;">
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- style -->
+ <link rel='stylesheet' type='text/css' href='http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?style' media='screen' />
+
+ <!-- font -->
+ <style>
+ @font-face {
+ font-family: "foofont";
+ src: url('http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?font');
+ }
+ .div_foo { font-family: "foofont"; }
+ </style>
+</head>
+<body>
+
+ <!-- images: -->
+ <img src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?img"></img>
+
+ <!-- redirects: upgrade http:// to https:// redirect to http:// and then upgrade to https:// again -->
+ <img src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?redirect-image"></img>
+
+ <!-- script: -->
+ <script src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?script"></script>
+
+ <!-- media: -->
+ <audio src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?media"></audio>
+
+ <!-- objects: -->
+ <object width="10" height="10" data="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?object"></object>
+
+ <!-- font: (apply font loaded in header to div) -->
+ <div class="div_foo">foo</div>
+
+ <!-- iframe: (same origin) -->
+ <iframe src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?iframe">
+ <!-- within that iframe we load an image over http and make sure the requested gets upgraded to https -->
+ </iframe>
+
+ <!-- xhr: -->
+ <script type="application/javascript">
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?xhr");
+ myXHR.send(null);
+ </script>
+
+ <!-- websockets: upgrade ws:// to wss://-->
+ <script type="application/javascript">
+ var mySocket = new WebSocket("ws://example.com/tests/dom/security/test/csp/file_upgrade_insecure");
+ mySocket.onopen = function(e) {
+ if (mySocket.url.includes("wss://")) {
+ window.parent.postMessage({result: "websocket-ok"}, "*");
+ }
+ else {
+ window.parent.postMessage({result: "websocket-error"}, "*");
+ }
+ };
+ mySocket.onerror = function(e) {
+ window.parent.postMessage({result: "websocket-unexpected-error"}, "*");
+ };
+ </script>
+
+ <!-- form action: (upgrade POST from http:// to https://) -->
+ <iframe name='formFrame' id='formFrame'></iframe>
+ <form target="formFrame" action="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?form" method="POST">
+ <input name="foo" value="foo">
+ <input type="submit" id="submitButton" formenctype='multipart/form-data' value="Submit form">
+ </form>
+ <script type="text/javascript">
+ var submitButton = document.getElementById('submitButton');
+ submitButton.click();
+ </script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_upgrade_insecure_referrer.sjs b/dom/security/test/csp/file_upgrade_insecure_referrer.sjs
new file mode 100644
index 000000000..e149afa4b
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_referrer.sjs
@@ -0,0 +1,55 @@
+// special *.sjs specifically customized for the needs of
+// Bug 1139297 and Bug 663570
+
+const PRE_HEAD =
+ "<!DOCTYPE HTML>" +
+ "<html>" +
+ "<head>";
+
+ const POST_HEAD =
+ "<meta charset='utf-8'>" +
+ "<title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>" +
+ "</head>" +
+ "<body>" +
+ "<img id='testimage' src='http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_referrer_server.sjs?img'></img>" +
+ "</body>" +
+ "</html>";
+
+const PRE_CSP = "upgrade-insecure-requests; default-src https:; ";
+const CSP_REFERRER_ORIGIN = "referrer origin";
+const CSP_REFEFFER_NO_REFERRER = "referrer no-referrer";
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ var queryString = request.queryString;
+
+ if (queryString === "test1") {
+ response.setHeader("Content-Security-Policy", PRE_CSP + CSP_REFERRER_ORIGIN, false);
+ response.write(PRE_HEAD + POST_HEAD);
+ return;
+ }
+
+ if (queryString === "test2") {
+ response.setHeader("Content-Security-Policy", PRE_CSP + CSP_REFEFFER_NO_REFERRER, false);
+ response.write(PRE_HEAD + POST_HEAD);
+ return;
+ }
+
+ if (queryString === "test3") {
+ var metacsp = "<meta http-equiv=\"Content-Security-Policy\" content = \"" + PRE_CSP + CSP_REFERRER_ORIGIN + "\" >";
+ response.write(PRE_HEAD + metacsp + POST_HEAD);
+ return;
+ }
+
+ if (queryString === "test4") {
+ var metacsp = "<meta http-equiv=\"Content-Security-Policy\" content = \"" + PRE_CSP + CSP_REFEFFER_NO_REFERRER + "\" >";
+ response.write(PRE_HEAD + metacsp + POST_HEAD);
+ return;
+ }
+
+ // we should never get here, but just in case return
+ // something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_referrer_server.sjs b/dom/security/test/csp/file_upgrade_insecure_referrer_server.sjs
new file mode 100644
index 000000000..be1e6da0c
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_referrer_server.sjs
@@ -0,0 +1,56 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 1139297 - Implement CSP upgrade-insecure-requests directive
+
+// small red image
+const IMG_BYTES = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ var queryString = request.queryString;
+
+ // (1) lets process the queryresult request async and
+ // wait till we have received the image request.
+ if (queryString == "queryresult") {
+ response.processAsync();
+ setObjectState("queryResult", response);
+ return;
+ }
+
+ // (2) Handle the image request and return the referrer
+ // result back to the stored queryresult request.
+ if (request.queryString == "img") {
+ response.setHeader("Content-Type", "image/png");
+ response.write(IMG_BYTES);
+
+ let referrer = "";
+ try {
+ referrer = request.getHeader("referer");
+ } catch (e) {
+ referrer = "";
+ }
+ // make sure the received image request was upgraded to https,
+ // otherwise we return not only the referrer but also indicate
+ // that the request was not upgraded to https. Note, that
+ // all upgrades happen in the browser before any non-secure
+ // request hits the wire.
+ referrer += (request.scheme == "https") ?
+ "" : " but request is not https";
+
+ getObjectState("queryResult", function(queryResponse) {
+ if (!queryResponse) {
+ return;
+ }
+ queryResponse.write(referrer);
+ queryResponse.finish();
+ });
+ return;
+ }
+
+ // we should not get here ever, but just in case return
+ // something unexpected.
+ response.write("doh!");
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_reporting.html b/dom/security/test/csp/file_upgrade_insecure_reporting.html
new file mode 100644
index 000000000..c78e9a784
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_reporting.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+</head>
+<body>
+
+ <!-- upgrade img from http:// to https:// -->
+ <img id="testimage" src="http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs?img"></img>
+
+ <script type="application/javascript">
+ var myImg = document.getElementById("testimage");
+ myImg.onload = function(e) {
+ window.parent.postMessage({result: "img-ok"}, "*");
+ };
+ myImg.onerror = function(e) {
+ window.parent.postMessage({result: "img-error"}, "*");
+ };
+ </script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs b/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs
new file mode 100644
index 000000000..b9940a7fd
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs
@@ -0,0 +1,80 @@
+// Custom *.sjs specifically for the needs of Bug
+// Bug 1139297 - Implement CSP upgrade-insecure-requests directive
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// small red image
+const IMG_BYTES = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
+
+const REPORT_URI = "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs?report";
+const POLICY = "upgrade-insecure-requests; default-src https: 'unsafe-inline'";
+const POLICY_RO = "default-src https: 'unsafe-inline'; report-uri " + REPORT_URI;
+
+function loadHTMLFromFile(path) {
+ // Load the HTML to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ var testHTMLFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testHTMLFile.append(dirs[i]);
+ }
+ var testHTMLFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ testHTMLFileStream.init(testHTMLFile, -1, 0, 0);
+ var testHTML = NetUtil.readInputStreamToString(testHTMLFileStream, testHTMLFileStream.available());
+ return testHTML;
+}
+
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // (1) Store the query that will report back whether the violation report was received
+ if (request.queryString == "queryresult") {
+ response.processAsync();
+ setObjectState("queryResult", response);
+ return;
+ }
+
+ // (2) We load a page using a CSP and a report only CSP
+ if (request.queryString == "toplevel") {
+ response.setHeader("Content-Security-Policy", POLICY, false);
+ response.setHeader("Content-Security-Policy-Report-Only", POLICY_RO, false);
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(loadHTMLFromFile("tests/dom/security/test/csp/file_upgrade_insecure_reporting.html"));
+ return;
+ }
+
+ // (3) Return the image back to the client
+ if (request.queryString == "img") {
+ response.setHeader("Content-Type", "image/png");
+ response.write(IMG_BYTES);
+ return;
+ }
+
+ // (4) Finally we receive the report, let's return the request from (1)
+ // signaling that we received the report correctly
+ if (request.queryString == "report") {
+ getObjectState("queryResult", function(queryResponse) {
+ if (!queryResponse) {
+ return;
+ }
+ queryResponse.write("report-ok");
+ queryResponse.finish();
+ });
+ return;
+ }
+
+ // we should never get here, but just in case ...
+ response.setHeader("Content-Type", "text/plain");
+ response.write("doh!");
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_server.sjs b/dom/security/test/csp/file_upgrade_insecure_server.sjs
new file mode 100644
index 000000000..e27b97830
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_server.sjs
@@ -0,0 +1,102 @@
+// Custom *.sjs file specifically for the needs of Bug:
+// Bug 1139297 - Implement CSP upgrade-insecure-requests directive
+
+const TOTAL_EXPECTED_REQUESTS = 11;
+
+const IFRAME_CONTENT =
+ "<!DOCTYPE HTML>" +
+ "<html>" +
+ "<head><meta charset='utf-8'>" +
+ "<title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>" +
+ "</head>" +
+ "<body>" +
+ "<img src='http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?nested-img'></img>" +
+ "</body>" +
+ "</html>";
+
+const expectedQueries = [ "script", "style", "img", "iframe", "form", "xhr",
+ "media", "object", "font", "img-redir", "nested-img"];
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ var queryString = request.queryString;
+
+ // initialize server variables and save the object state
+ // of the initial request, which returns async once the
+ // server has processed all requests.
+ if (queryString == "queryresult") {
+ setState("totaltests", TOTAL_EXPECTED_REQUESTS.toString());
+ setState("receivedQueries", "");
+ response.processAsync();
+ setObjectState("queryResult", response);
+ return;
+ }
+
+ // handle img redirect (https->http)
+ if (queryString == "redirect-image") {
+ var newLocation =
+ "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_server.sjs?img-redir";
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", newLocation, false);
+ return;
+ }
+
+ // just in case error handling for unexpected queries
+ if (expectedQueries.indexOf(queryString) == -1) {
+ response.write("doh!");
+ return;
+ }
+
+ // make sure all the requested queries are indeed https
+ queryString += (request.scheme == "https") ? "-ok" : "-error";
+
+ var receivedQueries = getState("receivedQueries");
+
+ // images, scripts, etc. get queried twice, do not
+ // confuse the server by storing the preload as
+ // well as the actual load. If either the preload
+ // or the actual load is not https, then we would
+ // append "-error" in the array and the test would
+ // fail at the end.
+ if (receivedQueries.includes(queryString)) {
+ return;
+ }
+
+ // append the result to the total query string array
+ if (receivedQueries != "") {
+ receivedQueries += ",";
+ }
+ receivedQueries += queryString;
+ setState("receivedQueries", receivedQueries);
+
+ // keep track of how many more requests the server
+ // is expecting
+ var totaltests = parseInt(getState("totaltests"));
+ totaltests -= 1;
+ setState("totaltests", totaltests.toString());
+
+ // return content (img) for the nested iframe to test
+ // that subresource requests within nested contexts
+ // get upgraded as well. We also have to return
+ // the iframe context in case of an error so we
+ // can test both, using upgrade-insecure as well
+ // as the base case of not using upgrade-insecure.
+ if ((queryString == "iframe-ok") || (queryString == "iframe-error")) {
+ response.write(IFRAME_CONTENT);
+ }
+
+ // if we have received all the requests, we return
+ // the result back.
+ if (totaltests == 0) {
+ getObjectState("queryResult", function(queryResponse) {
+ if (!queryResponse) {
+ return;
+ }
+ var receivedQueries = getState("receivedQueries");
+ queryResponse.write(receivedQueries);
+ queryResponse.finish();
+ });
+ }
+}
diff --git a/dom/security/test/csp/file_upgrade_insecure_wsh.py b/dom/security/test/csp/file_upgrade_insecure_wsh.py
new file mode 100644
index 000000000..0ae161bff
--- /dev/null
+++ b/dom/security/test/csp/file_upgrade_insecure_wsh.py
@@ -0,0 +1,7 @@
+from mod_pywebsocket import msgutil
+
+def web_socket_do_extra_handshake(request):
+ pass
+
+def web_socket_transfer_data(request):
+ pass
diff --git a/dom/security/test/csp/file_web_manifest.html b/dom/security/test/csp/file_web_manifest.html
new file mode 100644
index 000000000..0f6a67460
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+<link rel="manifest" href="file_web_manifest.json">
+</head>
+<h1>Support Page for Web Manifest Tests</h1> \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest.json b/dom/security/test/csp/file_web_manifest.json
new file mode 100644
index 000000000..917ab73ef
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest.json
@@ -0,0 +1 @@
+{"name": "loaded"} \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest.json^headers^ b/dom/security/test/csp/file_web_manifest.json^headers^
new file mode 100644
index 000000000..e0e00c4be
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest.json^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://example.org \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest_https.html b/dom/security/test/csp/file_web_manifest_https.html
new file mode 100644
index 000000000..b0ff9ef85
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest_https.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="manifest" href="https://example.com:443/tests/dom/security/test/csp/file_web_manifest_https.json">
+<h1>Support Page for Web Manifest Tests</h1> \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest_https.json b/dom/security/test/csp/file_web_manifest_https.json
new file mode 100644
index 000000000..917ab73ef
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest_https.json
@@ -0,0 +1 @@
+{"name": "loaded"} \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest_mixed_content.html b/dom/security/test/csp/file_web_manifest_mixed_content.html
new file mode 100644
index 000000000..55f17c0f9
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest_mixed_content.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+<link
+ rel="manifest"
+ href="http://example.org/tests/dom/security/test/csp/file_testserver.sjs?file=/test/dom/security/test/csp/file_web_manifest.json&amp;cors=*">
+</head>
+<h1>Support Page for Web Manifest Tests</h1>
+<p>Used to try to load a resource over an insecure connection to trigger mixed content blocking.</p> \ No newline at end of file
diff --git a/dom/security/test/csp/file_web_manifest_remote.html b/dom/security/test/csp/file_web_manifest_remote.html
new file mode 100644
index 000000000..7ecf8eec4
--- /dev/null
+++ b/dom/security/test/csp/file_web_manifest_remote.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<link rel="manifest"
+ crossorigin
+ href="//mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs?file=/tests/dom/security/test/csp/file_web_manifest.json&amp;cors=*">
+
+<h1>Support Page for Web Manifest Tests</h1>
+<p>Loads a manifest from mochi.test:8888 with CORS set to "*".</p> \ No newline at end of file
diff --git a/dom/security/test/csp/mochitest.ini b/dom/security/test/csp/mochitest.ini
new file mode 100644
index 000000000..8add999c3
--- /dev/null
+++ b/dom/security/test/csp/mochitest.ini
@@ -0,0 +1,300 @@
+[DEFAULT]
+support-files =
+ file_base_uri_server.sjs
+ file_blob_data_schemes.html
+ file_connect-src.html
+ file_connect-src-fetch.html
+ file_CSP.css
+ file_CSP.sjs
+ file_allow_https_schemes.html
+ file_bug663567.xsl
+ file_bug663567_allows.xml
+ file_bug663567_allows.xml^headers^
+ file_bug663567_blocks.xml
+ file_bug663567_blocks.xml^headers^
+ file_bug802872.html
+ file_bug802872.html^headers^
+ file_bug802872.js
+ file_bug802872.sjs
+ file_bug885433_allows.html
+ file_bug885433_allows.html^headers^
+ file_bug885433_blocks.html
+ file_bug885433_blocks.html^headers^
+ file_bug888172.html
+ file_bug888172.sjs
+ file_evalscript_main.js
+ file_evalscript_main_allowed.js
+ file_evalscript_main.html
+ file_evalscript_main.html^headers^
+ file_evalscript_main_allowed.html
+ file_evalscript_main_allowed.html^headers^
+ file_frameancestors_main.html
+ file_frameancestors_main.js
+ file_frameancestors.sjs
+ file_inlinescript.html
+ file_inlinestyle_main.html
+ file_inlinestyle_main.html^headers^
+ file_inlinestyle_main_allowed.html
+ file_inlinestyle_main_allowed.html^headers^
+ file_invalid_source_expression.html
+ file_main.html
+ file_main.html^headers^
+ file_main.js
+ file_main_worker.js
+ file_main_worker.js^headers^
+ file_child_worker.js
+ file_child_worker.js^headers^
+ file_web_manifest.html
+ file_web_manifest_remote.html
+ file_web_manifest_https.html
+ file_web_manifest.json
+ file_web_manifest.json^headers^
+ file_web_manifest_https.json
+ file_web_manifest_mixed_content.html
+ file_bug836922_npolicies.html
+ file_bug836922_npolicies.html^headers^
+ file_bug836922_npolicies_ro_violation.sjs
+ file_bug836922_npolicies_violation.sjs
+ file_bug886164.html
+ file_bug886164.html^headers^
+ file_bug886164_2.html
+ file_bug886164_2.html^headers^
+ file_bug886164_3.html
+ file_bug886164_3.html^headers^
+ file_bug886164_4.html
+ file_bug886164_4.html^headers^
+ file_bug886164_5.html
+ file_bug886164_5.html^headers^
+ file_bug886164_6.html
+ file_bug886164_6.html^headers^
+ file_redirects_main.html
+ file_redirects_page.sjs
+ file_redirects_resource.sjs
+ file_bug910139.sjs
+ file_bug910139.xml
+ file_bug910139.xsl
+ file_bug909029_star.html
+ file_bug909029_star.html^headers^
+ file_bug909029_none.html
+ file_bug909029_none.html^headers^
+ file_bug1229639.html
+ file_bug1229639.html^headers^
+ file_bug1312272.html
+ file_bug1312272.js
+ file_bug1312272.html^headers^
+ file_policyuri_regression_from_multipolicy.html
+ file_policyuri_regression_from_multipolicy.html^headers^
+ file_policyuri_regression_from_multipolicy_policy
+ file_shouldprocess.html
+ file_nonce_source.html
+ file_nonce_source.html^headers^
+ file_bug941404.html
+ file_bug941404_xhr.html
+ file_bug941404_xhr.html^headers^
+ file_hash_source.html
+ file_dual_header_testserver.sjs
+ file_hash_source.html^headers^
+ file_scheme_relative_sources.js
+ file_scheme_relative_sources.sjs
+ file_ignore_unsafe_inline.html
+ file_ignore_unsafe_inline_multiple_policies_server.sjs
+ file_self_none_as_hostname_confusion.html
+ file_self_none_as_hostname_confusion.html^headers^
+ file_path_matching.html
+ file_path_matching_incl_query.html
+ file_path_matching.js
+ file_path_matching_redirect.html
+ file_path_matching_redirect_server.sjs
+ file_testserver.sjs
+ file_report_uri_missing_in_report_only_header.html
+ file_report_uri_missing_in_report_only_header.html^headers^
+ file_report.html
+ file_report_chromescript.js
+ file_redirect_content.sjs
+ file_redirect_report.sjs
+ file_subframe_run_js_if_allowed.html
+ file_subframe_run_js_if_allowed.html^headers^
+ file_leading_wildcard.html
+ file_multi_policy_injection_bypass.html
+ file_multi_policy_injection_bypass.html^headers^
+ file_multi_policy_injection_bypass_2.html
+ file_multi_policy_injection_bypass_2.html^headers^
+ file_null_baseuri.html
+ file_form-action.html
+ file_referrerdirective.html
+ referrerdirective.sjs
+ file_upgrade_insecure.html
+ file_upgrade_insecure_meta.html
+ file_upgrade_insecure_server.sjs
+ file_upgrade_insecure_wsh.py
+ file_upgrade_insecure_reporting.html
+ file_upgrade_insecure_reporting_server.sjs
+ file_upgrade_insecure_referrer.sjs
+ file_upgrade_insecure_referrer_server.sjs
+ file_upgrade_insecure_cors.html
+ file_upgrade_insecure_cors_server.sjs
+ file_report_for_import.css
+ file_report_for_import.html
+ file_report_for_import_server.sjs
+ file_service_worker.html
+ file_service_worker.js
+ file_child-src_iframe.html
+ file_child-src_inner_frame.html
+ file_child-src_worker.html
+ file_child-src_worker_data.html
+ file_child-src_worker-redirect.html
+ file_child-src_worker.js
+ file_child-src_service_worker.html
+ file_child-src_service_worker.js
+ file_child-src_shared_worker.html
+ file_child-src_shared_worker_data.html
+ file_child-src_shared_worker-redirect.html
+ file_child-src_shared_worker.js
+ file_redirect_worker.sjs
+ file_meta_element.html
+ file_meta_header_dual.sjs
+ file_docwrite_meta.html
+ file_doccomment_meta.html
+ file_docwrite_meta.css
+ file_docwrite_meta.js
+ file_multipart_testserver.sjs
+ file_fontloader.sjs
+ file_fontloader.woff
+ file_block_all_mcb.sjs
+ file_block_all_mixed_content_frame_navigation1.html
+ file_block_all_mixed_content_frame_navigation2.html
+ file_form_action_server.sjs
+ !/image/test/mochitest/blue.png
+ file_meta_whitespace_skipping.html
+ file_ping.html
+ test_iframe_sandbox_top_1.html^headers^
+ file_iframe_sandbox_document_write.html
+ file_sandbox_pass.js
+ file_sandbox_fail.js
+ file_sandbox_1.html
+ file_sandbox_2.html
+ file_sandbox_3.html
+ file_sandbox_4.html
+ file_sandbox_5.html
+ file_sandbox_6.html
+ file_sandbox_7.html
+ file_sandbox_8.html
+ file_sandbox_9.html
+ file_sandbox_10.html
+ file_sandbox_11.html
+ file_sandbox_12.html
+ file_sandbox_13.html
+ file_require_sri_meta.sjs
+ file_require_sri_meta.js
+ file_sendbeacon.html
+ file_upgrade_insecure_docwrite_iframe.sjs
+ file_data-uri_blocked.html
+ file_data-uri_blocked.html^headers^
+ file_strict_dynamic_js_url.html
+ file_strict_dynamic_script_events.html
+ file_strict_dynamic_script_events_xbl.html
+ file_strict_dynamic_script_inline.html
+ file_strict_dynamic_script_extern.html
+ file_strict_dynamic.js
+ file_strict_dynamic_parser_inserted_doc_write.html
+ file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html
+ file_strict_dynamic_non_parser_inserted.html
+ file_strict_dynamic_non_parser_inserted_inline.html
+ file_strict_dynamic_unsafe_eval.html
+ file_strict_dynamic_default_src.html
+ file_strict_dynamic_default_src.js
+ file_iframe_srcdoc.sjs
+ file_iframe_sandbox_srcdoc.html
+ file_iframe_sandbox_srcdoc.html^headers^
+
+[test_base-uri.html]
+[test_blob_data_schemes.html]
+[test_connect-src.html]
+[test_CSP.html]
+[test_allow_https_schemes.html]
+[test_bug663567.html]
+[test_bug802872.html]
+[test_bug885433.html]
+[test_bug888172.html]
+[test_evalscript.html]
+[test_frameancestors.html]
+skip-if = toolkit == 'android' # Times out, not sure why (bug 1008445)
+[test_inlinescript.html]
+[test_inlinestyle.html]
+[test_invalid_source_expression.html]
+[test_bug836922_npolicies.html]
+[test_bug886164.html]
+[test_redirects.html]
+[test_bug910139.html]
+[test_bug909029.html]
+[test_bug1229639.html]
+[test_policyuri_regression_from_multipolicy.html]
+[test_nonce_source.html]
+[test_bug941404.html]
+[test_form-action.html]
+[test_hash_source.html]
+[test_scheme_relative_sources.html]
+[test_ignore_unsafe_inline.html]
+[test_self_none_as_hostname_confusion.html]
+[test_path_matching.html]
+[test_path_matching_redirect.html]
+[test_report_uri_missing_in_report_only_header.html]
+[test_report.html]
+[test_301_redirect.html]
+[test_302_redirect.html]
+[test_303_redirect.html]
+[test_307_redirect.html]
+[test_subframe_run_js_if_allowed.html]
+[test_leading_wildcard.html]
+[test_multi_policy_injection_bypass.html]
+[test_null_baseuri.html]
+[test_referrerdirective.html]
+[test_dual_header.html]
+[test_upgrade_insecure.html]
+# no ssl support as well as websocket tests do not work (see test_websocket.html)
+skip-if = toolkit == 'android' || (os != 'linux' && !debug) # Bug 1316305, Bug 1183300
+[test_upgrade_insecure_reporting.html]
+skip-if = toolkit == 'android'
+[test_upgrade_insecure_referrer.html]
+skip-if = toolkit == 'android'
+[test_upgrade_insecure_cors.html]
+skip-if = toolkit == 'android'
+[test_report_for_import.html]
+[test_blocked_uri_in_reports.html]
+[test_service_worker.html]
+[test_child-src_worker.html]
+[test_shouldprocess.html]
+# Fennec platform does not support Java applet plugin
+skip-if = toolkit == 'android' #investigate in bug 1250814
+[test_child-src_worker_data.html]
+[test_child-src_worker-redirect.html]
+[test_child-src_iframe.html]
+[test_meta_element.html]
+[test_meta_header_dual.html]
+[test_docwrite_meta.html]
+[test_multipartchannel.html]
+[test_fontloader.html]
+[test_block_all_mixed_content.html]
+tags = mcb
+[test_block_all_mixed_content_frame_navigation.html]
+tags = mcb
+[test_form_action_blocks_url.html]
+[test_meta_whitespace_skipping.html]
+[test_iframe_sandbox.html]
+[test_iframe_sandbox_top_1.html]
+[test_sandbox.html]
+[test_ping.html]
+[test_require_sri_meta.html]
+[test_sendbeacon.html]
+[test_upgrade_insecure_docwrite_iframe.html]
+[test_bug1242019.html]
+[test_bug1312272.html]
+[test_strict_dynamic.html]
+[test_strict_dynamic_parser_inserted.html]
+[test_strict_dynamic_default_src.html]
+[test_iframe_sandbox_srcdoc.html]
+[test_iframe_srcdoc.html]
+[test_sandbox_allow_scripts.html]
+support-files =
+ file_sandbox_allow_scripts.html
+ file_sandbox_allow_scripts.html^headers^
diff --git a/dom/security/test/csp/referrerdirective.sjs b/dom/security/test/csp/referrerdirective.sjs
new file mode 100644
index 000000000..f238ab452
--- /dev/null
+++ b/dom/security/test/csp/referrerdirective.sjs
@@ -0,0 +1,36 @@
+// Used for bug 965727 to serve up really simple scripts reflecting the
+// referrer sent to load this back to the loader.
+
+
+function handleRequest(request, response) {
+ // skip speculative loads.
+
+ var splits = request.queryString.split('&');
+ var params = {};
+ splits.forEach(function(v) {
+ let parts = v.split('=');
+ params[parts[0]] = unescape(parts[1]);
+ });
+
+ var loadType = params['type'];
+ var referrerLevel = 'error';
+
+ if (request.hasHeader('Referer')) {
+ var referrer = request.getHeader('Referer');
+ if (referrer.indexOf("file_testserver.sjs") > -1) {
+ referrerLevel = "full";
+ } else {
+ referrerLevel = "origin";
+ }
+ } else {
+ referrerLevel = 'none';
+ }
+
+ var theScript = 'window.postResult("' + loadType + '", "' + referrerLevel + '");';
+ response.setHeader('Content-Type', 'application/javascript; charset=utf-8', false);
+ response.setHeader('Cache-Control', 'no-cache', false);
+
+ if (request.method != "OPTIONS") {
+ response.write(theScript);
+ }
+}
diff --git a/dom/security/test/csp/test_301_redirect.html b/dom/security/test/csp/test_301_redirect.html
new file mode 100644
index 000000000..8b625a401
--- /dev/null
+++ b/dom/security/test/csp/test_301_redirect.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+Test that CSP violation reports are not sent when a 301 redirect is encountered
+-->
+<head>
+ <title>Test for Bug 650386</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650386">Mozilla Bug 650386</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 650386 **/
+
+// This is used to watch the redirect of the report POST get blocked
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // this is used to fail the test - if we see the POST to the target of the redirect
+ // we know this is a fail
+ var uri = data;
+ if (uri == "http://example.com/some/fake/path")
+ window.done(false);
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // something was blocked, but we are looking specifically for the redirect being blocked
+ if (data == "denied redirect while sending violation report")
+ window.done(true);
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+// result == true if we saw the redirect blocked notify, false if we saw the post
+// to the redirect target go out
+window.done = function(result) {
+ ok(result, "a 301 redirect when posting violation report should be blocked");
+
+ // clean up observers and finish the test
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('content_iframe').src = 'file_redirect_content.sjs?301';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_302_redirect.html b/dom/security/test/csp/test_302_redirect.html
new file mode 100644
index 000000000..616ecd9eb
--- /dev/null
+++ b/dom/security/test/csp/test_302_redirect.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+Test that CSP violation reports are not sent when a 302 redirect is encountered
+-->
+<head>
+ <title>Test for Bug 650386</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650386">Mozilla Bug 650386</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 650386 **/
+
+// This is used to watch the redirect of the report POST get blocked
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // this is used to fail the test - if we see the POST to the target of the redirect
+ // we know this is a fail
+ var uri = data;
+ if (uri == "http://example.com/some/fake/path")
+ window.done(false);
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // something was blocked, but we are looking specifically for the redirect being blocked
+ if (data == "denied redirect while sending violation report")
+ window.done(true);
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+// result == true if we saw the redirect blocked notify, false if we saw the post
+// to the redirect target go out
+window.done = function(result) {
+ ok(result, "a 302 redirect when posting violation report should be blocked");
+
+ // clean up observers and finish the test
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('content_iframe').src = 'file_redirect_content.sjs?302';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_303_redirect.html b/dom/security/test/csp/test_303_redirect.html
new file mode 100644
index 000000000..9e59a18f6
--- /dev/null
+++ b/dom/security/test/csp/test_303_redirect.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+Test that CSP violation reports are not sent when a 303 redirect is encountered
+-->
+<head>
+ <title>Test for Bug 650386</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650386">Mozilla Bug 650386</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 650386 **/
+
+// This is used to watch the redirect of the report POST get blocked
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // this is used to fail the test - if we see the POST to the target of the redirect
+ // we know this is a fail
+ var uri = data;
+ if (uri == "http://example.com/some/fake/path")
+ window.done(false);
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // something was blocked, but we are looking specifically for the redirect being blocked
+ if (data == "denied redirect while sending violation report")
+ window.done(true);
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+// result == true if we saw the redirect blocked notify, false if we saw the post
+// to the redirect target go out
+window.done = function(result) {
+ ok(result, "a 303 redirect when posting violation report should be blocked");
+
+ // clean up observers and finish the test
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('content_iframe').src = 'file_redirect_content.sjs?303';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_307_redirect.html b/dom/security/test/csp/test_307_redirect.html
new file mode 100644
index 000000000..5e30b1b86
--- /dev/null
+++ b/dom/security/test/csp/test_307_redirect.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650386
+Test that CSP violation reports are not sent when a 307 redirect is encountered
+-->
+<head>
+ <title>Test for Bug 650386</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650386">Mozilla Bug 650386</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 650386 **/
+
+// This is used to watch the redirect of the report POST get blocked
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // this is used to fail the test - if we see the POST to the target of the redirect
+ // we know this is a fail
+ var uri = data;
+ if (uri == "http://example.com/some/fake/path")
+ window.done(false);
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // something was blocked, but we are looking specifically for the redirect being blocked
+ if (data == "denied redirect while sending violation report")
+ window.done(true);
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+// result == true if we saw the redirect blocked notify, false if we saw the post
+// to the redirect target go out
+window.done = function(result) {
+ ok(result, "a 307 redirect when posting violation report should be blocked");
+
+ // clean up observers and finish the test
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('content_iframe').src = 'file_redirect_content.sjs?307';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_CSP.html b/dom/security/test/csp/test_CSP.html
new file mode 100644
index 000000000..1cde9902d
--- /dev/null
+++ b/dom/security/test/csp/test_CSP.html
@@ -0,0 +1,147 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy Connections</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+window.tests = {
+ img_good: -1,
+ img_bad: -1,
+ style_good: -1,
+ style_bad: -1,
+ frame_good: -1,
+ frame_bad: -1,
+ script_good: -1,
+ script_bad: -1,
+ xhr_good: -1,
+ xhr_bad: -1,
+ fetch_good: -1,
+ fetch_bad: -1,
+ beacon_good: -1,
+ beacon_bad: -1,
+ worker_xhr_same_bad: -1,
+ worker_xhr_cross_good: -1,
+ worker_fetch_same_bad: -1,
+ worker_fetch_cross_good: -1,
+ worker_script_same_good: -1,
+ worker_script_cross_bad: -1,
+ worker_inherited_xhr_good: -1,
+ worker_inherited_xhr_bad: -1,
+ worker_inherited_fetch_good: -1,
+ worker_inherited_fetch_bad: -1,
+ worker_inherited_script_good: -1,
+ worker_inherited_script_bad: -1,
+ worker_child_xhr_same_bad: -1,
+ worker_child_xhr_cross_bad: -1,
+ worker_child_script_same_bad: -1,
+ worker_child_script_cross_bad: -1,
+ worker_child_inherited_parent_xhr_bad: -1,
+ worker_child_inherited_parent_xhr_good: -1,
+ worker_child_inherited_parent_script_good: -1,
+ worker_child_inherited_parent_script_bad: -1,
+ worker_child_inherited_document_xhr_good: -1,
+ worker_child_inherited_document_xhr_bad: -1,
+ worker_child_inherited_document_script_good: -1,
+ worker_child_inherited_document_script_bad: -1,
+ media_good: -1,
+ media_bad: -1,
+ font_good: -1,
+ font_bad: -1,
+ object_good: -1,
+ object_bad: -1,
+};
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be stopped!
+
+ // This is a special observer topic that is proxied from
+ // http-on-modify-request in the parent process to inform us when a URI is
+ // loaded
+ if (topic === "specialpowers-http-notify-request") {
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+
+ window.testResult(testid,
+ /_good/.test(testid),
+ uri + " allowed by csp");
+ }
+
+ if (topic === "csp-on-violate-policy") {
+ // these were blocked... record that they were blocked
+ var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /_bad/.test(testid),
+ asciiSpec + " blocked by \"" + data + "\"");
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+ // test already complete.... forget it... remember the first result.
+ if (window.tests[testname] != -1)
+ return;
+
+ ok(testname in window.tests, "It's a real test");
+ window.tests[testname] = result;
+ is(result, true, testname + ' test: ' + msg);
+
+ // if any test is incomplete, keep waiting
+ for (var v in window.tests)
+ if(tests[v] == -1)
+ return;
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+ {'set':[// This defaults to 0 ("preload none") on mobile (B2G/Android), which
+ // blocks loading the resource until the user interacts with a
+ // corresponding widget, which breaks the media_* tests. We set it
+ // back to the default used by desktop Firefox to get consistent
+ // behavior.
+ ["media.preload.default", 2]]},
+ function() {
+ // save this for last so that our listeners are registered.
+ // ... this loads the testbed of good and bad requests.
+ document.getElementById('cspframe').src = 'file_main.html';
+ });
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_allow_https_schemes.html b/dom/security/test/csp/test_allow_https_schemes.html
new file mode 100644
index 000000000..713464200
--- /dev/null
+++ b/dom/security/test/csp/test_allow_https_schemes.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 826805 - Allow http and https for scheme-less sources</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We are loading the following url (including a fragment portion):
+ * https://example.com/tests/dom/security/test/csp/file_path_matching.js#foo
+ * using different policies that lack specification of a scheme.
+ *
+ * Since the file is served over http:, the upgrade to https should be
+ * permitted by CSP in case no port is specified.
+ */
+
+var policies = [
+ ["allowed", "example.com"],
+ ["allowed", "example.com:443"],
+ ["allowed", "example.com:80"],
+ ["allowed", "http://*:80"],
+ ["allowed", "https://*:443"],
+ // our testing framework only supports :80 and :443, but
+ // using :8000 in a policy does the trick for the test.
+ ["blocked", "example.com:8000"],
+]
+
+var counter = 0;
+var policy;
+
+function loadNextTest() {
+ if (counter == policies.length) {
+ SimpleTest.finish();
+ }
+ else {
+ policy = policies[counter++];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_allow_https_schemes.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape("default-src 'none'; script-src " + policy[1]);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+ }
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, policy[0], "should be " + policy[0] + " in test " + (counter - 1) + "!");
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content in test " + (counter - 1) + "!");
+ }
+ loadNextTest();
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_base-uri.html b/dom/security/test/csp/test_base-uri.html
new file mode 100644
index 000000000..7b8dcf7e2
--- /dev/null
+++ b/dom/security/test/csp/test_base-uri.html
@@ -0,0 +1,124 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1045897 - Test CSP base-uri directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page in an iframe (served over http://example.com) that tries to
+ * modify the 'base' either through setting or also removing the base-uri. We
+ * load that page using different policies and verify that setting the base-uri
+ * is correctly blocked by CSP.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ { csp: "base-uri http://mochi.test;",
+ base1: "http://mochi.test",
+ base2: "",
+ action: "enforce-csp",
+ result: "http://mochi.test",
+ desc: "CSP allows base uri"
+ },
+ { csp: "base-uri http://example.com;",
+ base1: "http://mochi.test",
+ base2: "",
+ action: "enforce-csp",
+ result: "http://example.com",
+ desc: "CSP blocks base uri"
+ },
+ { csp: "base-uri https:",
+ base1: "http://mochi.test",
+ base2: "",
+ action: "enforce-csp",
+ result: "http://example.com",
+ desc: "CSP blocks http base"
+ },
+ { csp: "base-uri 'none'",
+ base1: "http://mochi.test",
+ base2: "",
+ action: "enforce-csp",
+ result: "http://example.com",
+ desc: "CSP allows no base modification"
+ },
+ { csp: "",
+ base1: "http://foo:foo/",
+ base2: "",
+ action: "enforce-csp",
+ result: "http://example.com",
+ desc: "Invalid base should be ignored"
+ },
+ { csp: "base-uri http://mochi.test",
+ base1: "http://mochi.test",
+ base2: "http://test1.example.com",
+ action: "remove-base1",
+ result: "http://example.com",
+ desc: "Removing first base should result in fallback base"
+ },
+ { csp: "",
+ base1: "http://mochi.test",
+ base2: "http://test1.example.com",
+ action: "remove-base1",
+ result: "http://test1.example.com",
+ desc: "Removing first base should result in the second base"
+ },
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to bubble up results back to this main page.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ // we only care about the base uri, so instead of comparing the complete uri
+ // we just make sure that the base is correct which is sufficient here.
+ ok(result.startsWith(tests[counter].result),
+ `${tests[counter].desc}: Expected a base URI that starts
+ with ${tests[counter].result} but got ${result}`);
+ loadNextTest();
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ var src = "http://example.com/tests/dom/security/test/csp/file_base_uri_server.sjs";
+ // append the CSP that should be used to serve the file
+ // please note that we have to include 'unsafe-inline' to permit sending the postMessage
+ src += "?csp=" + escape("script-src 'unsafe-inline'; " + tests[counter].csp);
+ // append potential base tags
+ src += "&base1=" + escape(tests[counter].base1);
+ src += "&base2=" + escape(tests[counter].base2);
+ // append potential action
+ src += "&action=" + escape(tests[counter].action);
+
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_blob_data_schemes.html b/dom/security/test/csp/test_blob_data_schemes.html
new file mode 100644
index 000000000..37b327b10
--- /dev/null
+++ b/dom/security/test/csp/test_blob_data_schemes.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1086999 - Wildcard should not match blob:, data:</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load an image using a data: and a blob: scheme and make
+ * sure a CSP containing a single ASTERISK (*) does not whitelist
+ * those loads. The single ASTERISK character should not match a
+ * URI's scheme of a type designating globally unique identifier
+ * (such as blob:, data:, or filesystem:)
+ */
+
+var tests = [
+ {
+ policy : "default-src 'unsafe-inline' blob: data:",
+ expected : "allowed",
+ },
+ {
+ policy : "default-src 'unsafe-inline' *",
+ expected : "blocked"
+ }
+];
+
+var testIndex = 0;
+var messageCounter = 0;
+var curTest;
+
+// onError handler is over-reporting, hence we make sure that
+// we get an error for both testcases: data and blob before we
+// move on to the next test.
+var dataRan = false;
+var blobRan = false;
+
+// a postMessage handler to communicate the results back to the parent.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+ is(event.data.result, curTest.expected, event.data.scheme + " should be " + curTest.expected);
+
+ if (event.data.scheme === "data") {
+ dataRan = true;
+ }
+ if (event.data.scheme === "blob") {
+ blobRan = true;
+ }
+ if (dataRan && blobRan) {
+ loadNextTest();
+ }
+}
+
+function loadNextTest() {
+ if (testIndex === tests.length) {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+ return;
+ }
+
+ dataRan = false;
+ blobRan = false;
+
+ curTest = tests[testIndex++];
+ // reset the messageCounter to make sure we receive all the postMessages from the iframe
+ messageCounter = 0;
+
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_blob_data_schemes.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+
+ document.getElementById("testframe").src = src;
+}
+
+SimpleTest.waitForExplicitFinish();
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_block_all_mixed_content.html b/dom/security/test/csp/test_block_all_mixed_content.html
new file mode 100644
index 000000000..d5c4cda8b
--- /dev/null
+++ b/dom/security/test/csp/test_block_all_mixed_content.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the tests:
+ * Test 1:
+ * We load mixed display content in a frame using the CSP
+ * directive 'block-all-mixed-content' and observe that the image is blocked.
+ *
+ * Test 2:
+ * We load mixed display content in a frame using a CSP that allows the load
+ * and observe that the image is loaded.
+ *
+ * Test 3:
+ * We load mixed display content in a frame not using a CSP at all
+ * and observe that the image is loaded.
+ *
+ * Test 4:
+ * We load mixed display content in a frame using the CSP
+ * directive 'block-all-mixed-content' and observe that the image is blocked.
+ * Please note that Test 3 loads the image we are about to load in Test 4 into
+ * the img cache. Let's make sure the cached (mixed display content) image is
+ * not allowed to be loaded.
+ */
+
+const BASE_URI = "https://example.com/tests/dom/security/test/csp/";
+
+const tests = [
+ { // Test 1
+ query: "csp-block",
+ expected: "img-blocked",
+ description: "(csp-block) block-all-mixed content should block mixed display content"
+ },
+ { // Test 2
+ query: "csp-allow",
+ expected: "img-loaded",
+ description: "(csp-allow) mixed display content should be loaded"
+ },
+ { // Test 3
+ query: "no-csp",
+ expected: "img-loaded",
+ description: "(no-csp) mixed display content should be loaded"
+ },
+ { // Test 4
+ query: "csp-block",
+ expected: "img-blocked",
+ description: "(csp-block) block-all-mixed content should block insecure cache loads"
+ },
+ { // Test 5
+ query: "cspro-block",
+ expected: "img-loaded",
+ description: "(cspro-block) block-all-mixed in report only mode should not block"
+ },
+];
+
+var curTest;
+var counter = -1;
+
+function checkResults(result) {
+ is(result, curTest.expected, curTest.description);
+ loadNextTest();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+ return;
+ }
+ curTest = tests[counter];
+ testframe.src = BASE_URI + "file_block_all_mcb.sjs?" + curTest.query;
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+ { 'set': [["security.mixed_content.block_display_content", false]] },
+ function() { loadNextTest(); }
+);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_block_all_mixed_content_frame_navigation.html b/dom/security/test/csp/test_block_all_mixed_content_frame_navigation.html
new file mode 100644
index 000000000..c9d671fd7
--- /dev/null
+++ b/dom/security/test/csp/test_block_all_mixed_content_frame_navigation.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ *
+ * http://a.com embeds https://b.com.
+ * https://b.com has a CSP using 'block-all-mixed-content'.
+ * | site | http://a.com
+ * | embeds | https://b.com (uses block-all-mixed-content)
+ *
+ * The user navigates the embedded frame from
+ * https://b.com -> http://c.com.
+ * The test makes sure that such a navigation is not blocked
+ * by block-all-mixed-content.
+ */
+
+function checkResults(result) {
+ is(result, "frame-navigated", "frame should be allowed to be navigated");
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+SimpleTest.waitForExplicitFinish();
+// http://a.com loads https://b.com
+document.getElementById("testframe").src =
+ "https://example.com/tests/dom/security/test/csp/file_block_all_mixed_content_frame_navigation1.html";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_blocked_uri_in_reports.html b/dom/security/test/csp/test_blocked_uri_in_reports.html
new file mode 100644
index 000000000..f68d8c03f
--- /dev/null
+++ b/dom/security/test/csp/test_blocked_uri_in_reports.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1069762 - Check blocked-uri in csp-reports after redirect</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We try to load a script from:
+ * http://example.com/tests/dom/security/test/csp/file_path_matching_redirect_server.sjs
+ * which gets redirected to:
+ * http://test1.example.com/tests/dom/security//test/csp/file_path_matching.js
+ *
+ * The blocked-uri in the csp-report should be:
+ * test1.example.com
+ * instead of:
+ * http://test1.example.com/tests/com/security/test/csp/file_path_matching.js
+ *
+ * see also: http://www.w3.org/TR/CSP/#violation-reports
+ *
+ * Note, that we reuse the test-setup from
+ * test_path_matching_redirect.html
+ */
+
+const reportURI = "http://mochi.test:8888/foo.sjs";
+const policy = "script-src http://example.com; report-uri " + reportURI;
+const testfile = "tests/dom/security/test/csp/file_path_matching_redirect.html";
+
+var chromeScriptUrl = SimpleTest.getTestFileURL("file_report_chromescript.js");
+var script = SpecialPowers.loadChromeScript(chromeScriptUrl);
+
+script.addMessageListener('opening-request-completed', function ml(msg) {
+ if (msg.error) {
+ ok(false, "Could not query report (exception: " + msg.error + ")");
+ } else {
+ try {
+ var reportObj = JSON.parse(msg.report);
+ } catch (e) {
+ ok(false, "Could not parse JSON (exception: " + e + ")");
+ }
+ try {
+ var cspReport = reportObj["csp-report"];
+ // blocked-uri should only be the asciiHost instead of:
+ // http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js
+ is(cspReport["blocked-uri"], "http://test1.example.com", "Incorrect blocked-uri");
+ } catch (e) {
+ ok(false, "Could not query report (exception: " + e + ")");
+ }
+ }
+
+ script.removeMessageListener('opening-request-completed', ml);
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape(testfile);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(policy);
+
+ document.getElementById("cspframe").src = src;
+}
+
+runTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug1229639.html b/dom/security/test/csp/test_bug1229639.html
new file mode 100644
index 000000000..cd322d36d
--- /dev/null
+++ b/dom/security/test/csp/test_bug1229639.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1229639 - Percent encoded CSP path matching.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (data === 'http://mochi.test:8888/tests/dom/security/test/csp/%24.js') {
+ is(topic, "specialpowers-http-notify-request");
+ this.remove();
+ SimpleTest.finish();
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_bug1229639.html';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug1242019.html b/dom/security/test/csp/test_bug1242019.html
new file mode 100644
index 000000000..d57fa02bf
--- /dev/null
+++ b/dom/security/test/csp/test_bug1242019.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1242019
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1242019</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1242019">Mozilla Bug 1242019</a>
+<p id="display"></p>
+
+<iframe id="cspframe"></iframe>
+
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+function cleanup() {
+ SpecialPowers.postConsoleSentinel();
+ SimpleTest.finish();
+};
+
+var expectedURI = "data:image/png;base64,iVBORw0KGgoAAAANSU"
+
+SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
+ // look for the message with data uri and see the data uri is truncated to 40 chars
+ data_start = aMsg.message.indexOf(expectedURI)
+ if (data_start > -1) {
+ data_uri = "";
+ data_uri = aMsg.message.substr(data_start);
+ // this will either match the elipsis after the URI or the . at the end of the message
+ data_uri = data_uri.substr(0, data_uri.indexOf("."));
+ if (data_uri == "") {
+ return;
+ }
+
+ ok(data_uri.length == 40, "Data URI only shows 40 characters in the console");
+ SimpleTest.executeSoon(cleanup);
+ }
+});
+
+// set up and start testing
+SimpleTest.waitForExplicitFinish();
+document.getElementById('cspframe').src = 'file_data-uri_blocked.html';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug1312272.html b/dom/security/test/csp/test_bug1312272.html
new file mode 100644
index 000000000..2cbebb844
--- /dev/null
+++ b/dom/security/test/csp/test_bug1312272.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+
+ <title>Test for bug 1312272</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe id="cspframe" style="width:100%"></iframe>
+
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+function handler(evt) {
+ console.log(evt);
+ if (evt.data === "finish") {
+ ok(true, 'Other events continue to work fine.')
+ SimpleTest.finish();
+ //removeEventListener('message', handler);
+ } else {
+ ok(false, "Should not get any other message")
+ }
+}
+var cspframe = document.getElementById("cspframe");
+cspframe.src = "file_bug1312272.html";
+addEventListener("message", handler);
+console.log("assignign frame");
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug663567.html b/dom/security/test/csp/test_bug663567.html
new file mode 100644
index 000000000..293aa2914
--- /dev/null
+++ b/dom/security/test/csp/test_bug663567.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test if XSLT stylesheet is subject to document's CSP</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <iframe style="width:100%;" id='xsltframe'></iframe>
+ <iframe style="width:100%;" id='xsltframe2'></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// define the expected output of this test
+var header = "this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!";
+
+var finishedTests = 0;
+var numberOfTests = 2;
+
+var checkExplicitFinish = function() {
+ finishedTests++;
+ if (finishedTests == numberOfTests) {
+ SimpleTest.finish();
+ }
+}
+
+function checkAllowed () {
+ /* The policy for this test is:
+ * Content-Security-Policy: default-src 'self'
+ *
+ * we load the xsl file using:
+ * <?xml-stylesheet type="text/xsl" href="file_bug663467.xsl"?>
+ */
+ try {
+ var cspframe = document.getElementById('xsltframe');
+ var xsltAllowedHeader = cspframe.contentWindow.document.getElementById('xsltheader').innerHTML;
+ is(xsltAllowedHeader, header, "XSLT loaded from 'self' should be allowed!");
+ }
+ catch (e) {
+ ok(false, "Error: could not access content in xsltframe!")
+ }
+ checkExplicitFinish();
+}
+
+function checkBlocked () {
+ /* The policy for this test is:
+ * Content-Security-Policy: default-src *.example.com
+ *
+ * we load the xsl file using:
+ * <?xml-stylesheet type="text/xsl" href="file_bug663467.xsl"?>
+ */
+ try {
+ var cspframe = document.getElementById('xsltframe2');
+ var xsltBlockedHeader = cspframe.contentWindow.document.getElementById('xsltheader');
+ is(xsltBlockedHeader, null, "XSLT loaded from different host should be blocked!");
+ }
+ catch (e) {
+ ok(false, "Error: could not access content in xsltframe2!")
+ }
+ checkExplicitFinish();
+}
+
+document.getElementById('xsltframe').addEventListener('load', checkAllowed, false);
+document.getElementById('xsltframe').src = 'file_bug663567_allows.xml';
+
+document.getElementById('xsltframe2').addEventListener('load', checkBlocked, false);
+document.getElementById('xsltframe2').src = 'file_bug663567_blocks.xml';
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug802872.html b/dom/security/test/csp/test_bug802872.html
new file mode 100644
index 000000000..70584c14f
--- /dev/null
+++ b/dom/security/test/csp/test_bug802872.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 802872</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <iframe style="width:100%;" id='eventframe'></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var finishedTests = 0;
+var numberOfTests = 2;
+
+var checkExplicitFinish = function () {
+ finishedTests++;
+ if (finishedTests == numberOfTests) {
+ SimpleTest.finish();
+ }
+}
+
+// add event listeners for CSP-permitted EventSrc callbacks
+addEventListener('allowedEventSrcCallbackOK', function (e) {
+ ok(true, "OK: CSP allows EventSource for whitelisted domain!");
+ checkExplicitFinish();
+}, false);
+addEventListener('allowedEventSrcCallbackFailed', function (e) {
+ ok(false, "Error: CSP blocks EventSource for whitelisted domain!");
+ checkExplicitFinish();
+}, false);
+
+// add event listeners for CSP-blocked EventSrc callbacks
+addEventListener('blockedEventSrcCallbackOK', function (e) {
+ ok(false, "Error: CSP allows EventSource to not whitelisted domain!");
+ checkExplicitFinish();
+}, false);
+addEventListener('blockedEventSrcCallbackFailed', function (e) {
+ ok(true, "OK: CSP blocks EventSource for not whitelisted domain!");
+ checkExplicitFinish();
+}, false);
+
+// load it
+document.getElementById('eventframe').src = 'file_bug802872.html';
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug836922_npolicies.html b/dom/security/test/csp/test_bug836922_npolicies.html
new file mode 100644
index 000000000..8d0390eed
--- /dev/null
+++ b/dom/security/test/csp/test_bug836922_npolicies.html
@@ -0,0 +1,240 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy multiple policy support (regular and Report-Only mode)</title>
+ <script type="text/javascript" src="/MochiKit/packed.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/dom/security/test/csp/";
+
+// These are test results: verified indicates whether or not the test has run.
+// true/false is the pass/fail result.
+window.loads = {
+ css_self: {expected: true, verified: false},
+ img_self: {expected: false, verified: false},
+ script_self: {expected: true, verified: false},
+};
+
+window.violation_reports = {
+ css_self:
+ {expected: 0, expected_ro: 0}, /* totally fine */
+ img_self:
+ {expected: 1, expected_ro: 0}, /* violates enforced CSP */
+ script_self:
+ {expected: 0, expected_ro: 1}, /* violates report-only */
+};
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire. This also watches for violation reports to go out.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ if (topic === "specialpowers-http-notify-request") {
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+
+ // violation reports don't come through here, but the requested resources do
+ // if the test has already finished, move on. Some things throw multiple
+ // requests (preloads and such)
+ try {
+ if (window.loads[testid].verified) return;
+ } catch(e) { return; }
+
+ // these are requests that were allowed by CSP
+ var testid = testpat.exec(uri)[1];
+ window.testResult(testid, 'allowed', uri + " allowed by csp");
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // if the violated policy was report-only, the resource will still be
+ // loaded even if this topic is notified.
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+
+ // if the test has already finished, move on.
+ try {
+ if (window.loads[testid].verified) return;
+ } catch(e) { return; }
+
+ // record the ones that were supposed to be blocked, but don't use this
+ // as an indicator for tests that are not blocked but do generate reports.
+ // We skip recording the result if the load is expected since a
+ // report-only policy will generate a request *and* a violation note.
+ if (!window.loads[testid].expected) {
+ window.testResult(testid,
+ 'blocked',
+ asciiSpec + " blocked by \"" + data + "\"");
+ }
+ }
+
+ // if any test is unverified, keep waiting
+ for (var v in window.loads) {
+ if(!window.loads[v].verified) {
+ return;
+ }
+ }
+
+ window.bug836922examiner.remove();
+ window.resultPoller.pollForFinish();
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.bug836922examiner = new examiner();
+
+
+// Poll for results and see if enough reports came in. Keep trying
+// for a few seconds before failing with lack of reports.
+// Have to do this because there's a race between the async reporting
+// and this test finishing, and we don't want to win the race.
+window.resultPoller = {
+
+ POLL_ATTEMPTS_LEFT: 14,
+
+ pollForFinish:
+ function() {
+ var vr = resultPoller.tallyReceivedReports();
+ if (resultPoller.verifyReports(vr, resultPoller.POLL_ATTEMPTS_LEFT < 1)) {
+ // report success condition.
+ resultPoller.resetReportServer();
+ SimpleTest.finish();
+ } else {
+ resultPoller.POLL_ATTEMPTS_LEFT--;
+ // try again unless we reached the threshold.
+ setTimeout(resultPoller.pollForFinish, 100);
+ }
+ },
+
+ resetReportServer:
+ function() {
+ var xhr = new XMLHttpRequest();
+ var xhr_ro = new XMLHttpRequest();
+ xhr.open("GET", "file_bug836922_npolicies_violation.sjs?reset", false);
+ xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?reset", false);
+ xhr.send(null);
+ xhr_ro.send(null);
+ },
+
+ tallyReceivedReports:
+ function() {
+ var xhr = new XMLHttpRequest();
+ var xhr_ro = new XMLHttpRequest();
+ xhr.open("GET", "file_bug836922_npolicies_violation.sjs?results", false);
+ xhr_ro.open("GET", "file_bug836922_npolicies_ro_violation.sjs?results", false);
+ xhr.send(null);
+ xhr_ro.send(null);
+
+ var received = JSON.parse(xhr.responseText);
+ var received_ro = JSON.parse(xhr_ro.responseText);
+
+ var results = {enforced: {}, reportonly: {}};
+ for (var r in window.violation_reports) {
+ results.enforced[r] = 0;
+ results.reportonly[r] = 0;
+ }
+
+ for (var r in received) {
+ results.enforced[r] += received[r];
+ }
+ for (var r in received_ro) {
+ results.reportonly[r] += received_ro[r];
+ }
+
+ return results;
+ },
+
+ verifyReports:
+ function(receivedCounts, lastAttempt) {
+ for (var r in window.violation_reports) {
+ var exp = window.violation_reports[r].expected;
+ var exp_ro = window.violation_reports[r].expected_ro;
+ var rec = receivedCounts.enforced[r];
+ var rec_ro = receivedCounts.reportonly[r];
+
+ // if this test breaks, these are helpful dumps:
+ //dump(">>> Verifying " + r + "\n");
+ //dump(" > Expected: " + exp + " / " + exp_ro + " (ro)\n");
+ //dump(" > Received: " + rec + " / " + rec_ro + " (ro) \n");
+
+ // in all cases, we're looking for *at least* the expected number of
+ // reports of each type (there could be more in some edge cases).
+ // If there are not enough, we keep waiting and poll the server again
+ // later. If there are enough, we can successfully finish.
+
+ if (exp == 0)
+ is(rec, 0,
+ "Expected zero enforced-policy violation " +
+ "reports for " + r + ", got " + rec);
+ else if (lastAttempt)
+ ok(rec >= exp,
+ "Received (" + rec + "/" + exp + ") " +
+ "enforced-policy reports for " + r);
+ else if (rec < exp)
+ return false; // continue waiting for more
+
+ if(exp_ro == 0)
+ is(rec_ro, 0,
+ "Expected zero report-only-policy violation " +
+ "reports for " + r + ", got " + rec_ro);
+ else if (lastAttempt)
+ ok(rec_ro >= exp_ro,
+ "Received (" + rec_ro + "/" + exp_ro + ") " +
+ "report-only-policy reports for " + r);
+ else if (rec_ro < exp_ro)
+ return false; // continue waiting for more
+ }
+
+ // if we complete the loop, we've found all of the violation
+ // reports we expect.
+ if (lastAttempt) return true;
+
+ // Repeat successful tests once more to record successes via ok()
+ return resultPoller.verifyReports(receivedCounts, true);
+ }
+};
+
+window.testResult = function(testname, result, msg) {
+ // otherwise, make sure the allowed ones are expected and blocked ones are not.
+ if (window.loads[testname].expected) {
+ is(result, 'allowed', ">> " + msg);
+ } else {
+ is(result, 'blocked', ">> " + msg);
+ }
+ window.loads[testname].verified = true;
+}
+
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'http://mochi.test:8888' + path + 'file_bug836922_npolicies.html';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug885433.html b/dom/security/test/csp/test_bug885433.html
new file mode 100644
index 000000000..22db0ed24
--- /dev/null
+++ b/dom/security/test/csp/test_bug885433.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy inline stylesheets stuff</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:100%;" id='cspframe'></iframe>
+<iframe style="width:100%;" id='cspframe2'></iframe>
+<script class="testbody" type="text/javascript">
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+// utilities for check functions
+// black means the style wasn't applied, applied styles are green
+var green = 'rgb(0, 128, 0)';
+var black = 'rgb(0, 0, 0)';
+
+// We test both script and style execution by observing changes in computed styles
+function checkAllowed () {
+ var cspframe = document.getElementById('cspframe');
+ var color;
+
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-script-allowed')).color;
+ ok(color === green, "Inline script should be allowed");
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-eval-script-allowed')).color;
+ ok(color === green, "Eval should be allowed");
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-style-allowed')).color;
+ ok(color === green, "Inline style should be allowed");
+
+ document.getElementById('cspframe2').src = 'file_bug885433_blocks.html';
+ document.getElementById('cspframe2').addEventListener('load', checkBlocked, false);
+}
+
+function checkBlocked () {
+ var cspframe = document.getElementById('cspframe2');
+ var color;
+
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-script-blocked')).color;
+ ok(color === black, "Inline script should be blocked");
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-eval-script-blocked')).color;
+ ok(color === black, "Eval should be blocked");
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('unsafe-inline-style-blocked')).color;
+ ok(color === black, "Inline style should be blocked");
+
+ SimpleTest.finish();
+}
+
+document.getElementById('cspframe').src = 'file_bug885433_allows.html';
+document.getElementById('cspframe').addEventListener('load', checkAllowed, false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug886164.html b/dom/security/test/csp/test_bug886164.html
new file mode 100644
index 000000000..74fd98458
--- /dev/null
+++ b/dom/security/test/csp/test_bug886164.html
@@ -0,0 +1,172 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 886164 - Enforce CSP in sandboxed iframe</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:200px;height:200px;" id='cspframe' sandbox="allow-same-origin"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe4' sandbox></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe5' sandbox="allow-scripts"></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe6' sandbox="allow-same-origin allow-scripts"></iframe>
+<script class="testbody" type="text/javascript">
+
+
+var path = "/tests/dom/security/test/csp/";
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+window.tests = {
+ // sandbox allow-same-origin; 'self'
+ img_good: -1, // same origin
+ img_bad: -1, //example.com
+
+ // sandbox; 'self'
+ img2_bad: -1, //example.com
+ img2a_good: -1, // same origin & is image
+
+ // sandbox allow-same-origin; 'none'
+ img3_bad: -1,
+ img3a_bad: -1,
+
+ // sandbox; 'none'
+ img4_bad: -1,
+ img4a_bad: -1,
+
+ // sandbox allow-scripts; 'none' 'unsafe-inline'
+ img5_bad: -1,
+ img5a_bad: -1,
+ script5_bad: -1,
+ script5a_bad: -1,
+
+ // sandbox allow-same-origin allow-scripts; 'self' 'unsafe-inline'
+ img6_bad: -1,
+ script6_bad: -1,
+};
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var cspTestsDone = false;
+var iframeSandboxTestsDone = false;
+
+// iframe related
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests === 5) {
+ iframeSandboxTestsDone = true;
+ if (cspTestsDone) {
+ SimpleTest.finish();
+ }
+ }
+}
+
+
+//csp related
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be stopped!
+
+ if (topic === "specialpowers-http-notify-request") {
+ //these things were allowed by CSP
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+
+ window.testResult(testid,
+ /_good/.test(testid),
+ uri + " allowed by csp");
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ //these were blocked... record that they were blocked
+ var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /_bad/.test(testid),
+ asciiSpec + " blocked by \"" + data + "\"");
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+ //test already complete.... forget it... remember the first result.
+ if (window.tests[testname] != -1)
+ return;
+
+ window.tests[testname] = result;
+ ok(result, testname + ' test: ' + msg);
+
+ // if any test is incomplete, keep waiting
+ for (var v in window.tests)
+ if(tests[v] == -1)
+ return;
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ cspTestsDone = true;
+ if (iframeSandboxTestsDone) {
+ SimpleTest.finish();
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_bug886164.html';
+document.getElementById('cspframe2').src = 'file_bug886164_2.html';
+document.getElementById('cspframe3').src = 'file_bug886164_3.html';
+document.getElementById('cspframe4').src = 'file_bug886164_4.html';
+document.getElementById('cspframe5').src = 'file_bug886164_5.html';
+document.getElementById('cspframe6').src = 'file_bug886164_6.html';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug888172.html b/dom/security/test/csp/test_bug888172.html
new file mode 100644
index 000000000..200e8c942
--- /dev/null
+++ b/dom/security/test/csp/test_bug888172.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 888172 - CSP 1.0 does not process 'unsafe-inline' or 'unsafe-eval' for default-src</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:100%;" id='testframe1'></iframe>
+<iframe style="width:100%;" id='testframe2'></iframe>
+<iframe style="width:100%;" id='testframe3'></iframe>
+<script class="testbody" type="text/javascript">
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+// utilities for check functions
+// black means the style wasn't applied, applied styles are green
+var green = 'rgb(0, 128, 0)';
+var black = 'rgb(0, 0, 0)';
+
+function getElementColorById(doc, id) {
+ return window.getComputedStyle(doc.contentDocument.getElementById(id)).color;
+}
+
+// We test both script and style execution by observing changes in computed styles
+function checkDefaultSrcOnly() {
+ var testframe = document.getElementById('testframe1');
+
+ ok(getElementColorById(testframe, 'unsafe-inline-script') === green, "Inline script should be allowed");
+ ok(getElementColorById(testframe, 'unsafe-eval-script') === green, "Eval should be allowed");
+ ok(getElementColorById(testframe, 'unsafe-inline-style') === green, "Inline style should be allowed");
+
+ document.getElementById('testframe2').src = 'file_bug888172.sjs?csp=' +
+ escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'self'");
+ document.getElementById('testframe2').addEventListener('load', checkDefaultSrcWithScriptSrc, false);
+}
+
+function checkDefaultSrcWithScriptSrc() {
+ var testframe = document.getElementById('testframe2');
+
+ ok(getElementColorById(testframe, 'unsafe-inline-script') === black, "Inline script should be blocked");
+ ok(getElementColorById(testframe, 'unsafe-eval-script') === black, "Eval should be blocked");
+ ok(getElementColorById(testframe, 'unsafe-inline-style') === green, "Inline style should be allowed");
+
+ document.getElementById('testframe3').src = 'file_bug888172.sjs?csp=' +
+ escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self'");
+ document.getElementById('testframe3').addEventListener('load', checkDefaultSrcWithStyleSrc, false);
+}
+
+function checkDefaultSrcWithStyleSrc() {
+ var testframe = document.getElementById('testframe3');
+
+ ok(getElementColorById(testframe, 'unsafe-inline-script') === green, "Inline script should be allowed");
+ ok(getElementColorById(testframe, 'unsafe-eval-script') === green, "Eval should be allowed");
+ ok(getElementColorById(testframe, 'unsafe-inline-style') === black, "Inline style should be blocked");
+
+ // last test calls finish
+ SimpleTest.finish();
+}
+
+document.getElementById('testframe1').src = 'file_bug888172.sjs?csp=' +
+ escape("default-src 'self' 'unsafe-inline' 'unsafe-eval'");
+document.getElementById('testframe1').addEventListener('load', checkDefaultSrcOnly, false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug909029.html b/dom/security/test/csp/test_bug909029.html
new file mode 100644
index 000000000..aebfabf48
--- /dev/null
+++ b/dom/security/test/csp/test_bug909029.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Bug 909029 - CSP source-lists ignore some source expressions like 'unsafe-inline' when * or 'none' are used (e.g., style-src, script-src)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <div id=content style="visibility:hidden">
+ <iframe id=testframe1></iframe>
+ <iframe id=testframe2></iframe>
+ </div>
+ <script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+window.tests = {
+ starExternalStylesLoaded: -1,
+ starExternalImgLoaded: -1,
+ noneExternalStylesBlocked: -1,
+ noneExternalImgLoaded: -1,
+ starInlineStyleAllowed: -1,
+ starInlineScriptBlocked: -1,
+ noneInlineStyleAllowed: -1,
+ noneInlineScriptBlocked: -1
+}
+
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-zA-Z]+)");
+
+ if (topic === "specialpowers-http-notify-request") {
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+ window.testResult(testid,
+ /Loaded/.test(testid),
+ "resource loaded");
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // these were blocked... record that they were blocked
+ // try because the subject could be an nsIURI or an nsISupportsCString
+ try {
+ var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /Blocked/.test(testid),
+ "resource blocked by CSP");
+ } catch(e) {
+ // if that fails, the subject is probably a string. Strings are only
+ // reported for inline and eval violations. Since we are testing those
+ // via the observed effects of script on CSSOM, we can simply ignore
+ // these subjects.
+ }
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+ //dump("in testResult: testname = " + testname + "\n");
+
+ //test already complete.... forget it... remember the first result.
+ if (window.tests[testname] != -1)
+ return;
+
+ window.tests[testname] = result;
+ is(result, true, testname + ' test: ' + msg);
+
+ // if any test is incomplete, keep waiting
+ for (var v in window.tests)
+ if(tests[v] == -1)
+ return;
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+// Helpers for inline script/style checks
+var black = 'rgb(0, 0, 0)';
+var green = 'rgb(0, 128, 0)';
+function getElementColorById(doc, id) {
+ return window.getComputedStyle(doc.contentDocument.getElementById(id)).color;
+}
+
+function checkInlineWithStar() {
+ var testframe = document.getElementById('testframe1');
+ window.testResult("starInlineStyleAllowed",
+ getElementColorById(testframe, 'inline-style') === green,
+ "Inline styles should be allowed (style-src 'unsafe-inline' with star)");
+ window.testResult("starInlineScriptBlocked",
+ getElementColorById(testframe, 'inline-script') === black,
+ "Inline scripts should be blocked (style-src 'unsafe-inline' with star)");
+}
+
+function checkInlineWithNone() {
+ // If a directive has 'none' in addition to other sources, 'none' is ignored
+ // and the other sources are used. 'none' is only a valid source if it is
+ // used by itself.
+ var testframe = document.getElementById('testframe2');
+ window.testResult("noneInlineStyleAllowed",
+ getElementColorById(testframe, 'inline-style') === green,
+ "Inline styles should be allowed (style-src 'unsafe-inline' with none)");
+ window.testResult("noneInlineScriptBlocked",
+ getElementColorById(testframe, 'inline-script') === black,
+ "Inline scripts should be blocked (style-src 'unsafe-inline' with none)");
+}
+
+document.getElementById('testframe1').src = 'file_bug909029_star.html';
+document.getElementById('testframe1').addEventListener('load', checkInlineWithStar, false);
+document.getElementById('testframe2').src = 'file_bug909029_none.html';
+document.getElementById('testframe2').addEventListener('load', checkInlineWithNone, false);
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/test_bug910139.html b/dom/security/test/csp/test_bug910139.html
new file mode 100644
index 000000000..63a7f77d1
--- /dev/null
+++ b/dom/security/test/csp/test_bug910139.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>CSP should block XSLT as script, not as style</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <iframe style="width:100%;" id='xsltframe'></iframe>
+ <iframe style="width:100%;" id='xsltframe2'></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// define the expected output of this test
+var header = "this xml file should be formatted using an xsl file(lower iframe should contain xml dump)!";
+
+function checkAllowed () {
+ /* The policy for this test is:
+ * Content-Security-Policy: default-src 'self'; script-src 'self'
+ *
+ * we load the xsl file using:
+ * <?xml-stylesheet type="text/xsl" href="file_bug910139.xsl"?>
+ */
+ try {
+ var cspframe = document.getElementById('xsltframe');
+ var xsltAllowedHeader = cspframe.contentWindow.document.getElementById('xsltheader').innerHTML;
+ is(xsltAllowedHeader, header, "XSLT loaded from 'self' should be allowed!");
+ }
+ catch (e) {
+ ok(false, "Error: could not access content in xsltframe!")
+ }
+
+ // continue with the next test
+ document.getElementById('xsltframe2').addEventListener('load', checkBlocked, false);
+ document.getElementById('xsltframe2').src = 'file_bug910139.sjs';
+}
+
+function checkBlocked () {
+ /* The policy for this test is:
+ * Content-Security-Policy: default-src 'self'; script-src *.example.com
+ *
+ * we load the xsl file using:
+ * <?xml-stylesheet type="text/xsl" href="file_bug910139.xsl"?>
+ */
+ try {
+ var cspframe = document.getElementById('xsltframe2');
+ var xsltBlockedHeader = cspframe.contentWindow.document.getElementById('xsltheader');
+ is(xsltBlockedHeader, null, "XSLT loaded from different host should be blocked!");
+ }
+ catch (e) {
+ ok(false, "Error: could not access content in xsltframe2!")
+ }
+ SimpleTest.finish();
+}
+
+document.getElementById('xsltframe').addEventListener('load', checkAllowed, false);
+document.getElementById('xsltframe').src = 'file_bug910139.sjs';
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_bug941404.html b/dom/security/test/csp/test_bug941404.html
new file mode 100644
index 000000000..07f45d176
--- /dev/null
+++ b/dom/security/test/csp/test_bug941404.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 941404 - Data documents should not set CSP</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+
+var path = "/tests/dom/security/test/csp/";
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+window.tests = {
+ img_good: -1,
+ img2_good: -1,
+};
+
+
+//csp related
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be stopped!
+
+ if (topic === "specialpowers-http-notify-request") {
+ //these things were allowed by CSP
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+
+ window.testResult(testid,
+ /_good/.test(testid),
+ uri + " allowed by csp");
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ //these were blocked... record that they were blocked
+ var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /_bad/.test(testid),
+ asciiSpec + " blocked by \"" + data + "\"");
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+ //test already complete.... forget it... remember the first result.
+ if (window.tests[testname] != -1)
+ return;
+
+ window.tests[testname] = result;
+ is(result, true, testname + ' test: ' + msg);
+
+ // if any test is incomplete, keep waiting
+ for (var v in window.tests)
+ if(tests[v] == -1) {
+ console.log(v + " is not complete");
+ return;
+ }
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_bug941404.html';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_child-src_iframe.html b/dom/security/test/csp/test_child-src_iframe.html
new file mode 100644
index 000000000..b4ba36f89
--- /dev/null
+++ b/dom/security/test/csp/test_child-src_iframe.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1045891</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page with a given CSP and verify that child frames and workers are correctly
+ * evaluated through the "child-src" directive.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var IFRAME_SRC="file_child-src_iframe.html"
+
+var tests = {
+ 'same-src': {
+ id: "same-src",
+ file: IFRAME_SRC,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'star-src': {
+ id: "star-src",
+ file: IFRAME_SRC,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'other-src': {
+ id: "other-src",
+ file: IFRAME_SRC,
+ result : "blocked",
+ policy : "default-src http://mochi.test:8888; script-src 'unsafe-inline'; child-src http://www.example.com"
+ },
+ 'same-src-by-frame-src': {
+ id: "same-src-by-frame-src",
+ file: IFRAME_SRC,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'none'; frame-src http://mochi.test:8888"
+ },
+ 'star-src-by-frame-src': {
+ id: "star-src-by-frame-src",
+ file: IFRAME_SRC,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'none'; frame-src *"
+ },
+ 'other-src-by-frame-src': {
+ id: "other-src-by-frame-src",
+ file: IFRAME_SRC,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888; frame-src http://www.example.com"
+ },
+ 'none-src-by-frame-src': {
+ id: "none-src-by-frame-src",
+ file: "file_child-src_iframe.html",
+ file: IFRAME_SRC,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888; frame-src 'none'"
+ }
+};
+
+finished = {};
+
+function checkFinished() {
+ if (Object.keys(finished).length == Object.keys(tests).length) {
+ window.removeEventListener('message', recvMessage);
+ SimpleTest.finish();
+ }
+}
+
+function recvMessage(ev) {
+ is(ev.data.message, tests[ev.data.id].result, "CSP child-src test " + ev.data.id);
+ finished[ev.data.id] = ev.data.message;
+
+ checkFinished();
+}
+
+window.addEventListener('message', recvMessage, false);
+
+function loadNextTest() {
+ for (item in tests) {
+ test = tests[item];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + test.file);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(test.policy);
+ // add our identifier
+ src += "#" + escape(test.id);
+
+ content = document.getElementById('content');
+ testframe = document.createElement("iframe");
+ testframe.setAttribute('id', test.id);
+ content.appendChild(testframe);
+ testframe.src = src;
+ }
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_child-src_worker-redirect.html b/dom/security/test/csp/test_child-src_worker-redirect.html
new file mode 100644
index 000000000..dfb99149c
--- /dev/null
+++ b/dom/security/test/csp/test_child-src_worker-redirect.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ </div>
+
+ <script class="testbody" type="text/javascript">
+ /*
+ * Description of the test:
+ * We load a page with a given CSP and verify that child frames and workers are correctly
+ * evaluated through the "child-src" directive.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var WORKER_REDIRECT_TEST_FILE = "file_child-src_worker-redirect.html";
+ var SHARED_WORKER_REDIRECT_TEST_FILE = "file_child-src_shared_worker-redirect.html";
+
+ var tests = {
+ 'same-src-worker_redir-same': {
+ id: "same-src-worker_redir-same",
+ file: WORKER_REDIRECT_TEST_FILE,
+ result : "allowed",
+ redir: "same",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'same-src-worker_redir-other': {
+ id: "same-src-worker_redir-other",
+ file: WORKER_REDIRECT_TEST_FILE,
+ result : "blocked",
+ redir: "other",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'star-src-worker_redir-same': {
+ id: "star-src-worker_redir-same",
+ file: WORKER_REDIRECT_TEST_FILE,
+ redir: "same",
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src *"
+ },
+ 'other-src-worker_redir-same': {
+ id: "other-src-worker_redir-same",
+ file: WORKER_REDIRECT_TEST_FILE,
+ redir: "same",
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src https://www.example.org"
+ },
+ /* shared workers */
+ 'same-src-shared_worker_redir-same': {
+ id: "same-src-shared_worker_redir-same",
+ file: SHARED_WORKER_REDIRECT_TEST_FILE,
+ result : "allowed",
+ redir: "same",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'same-src-shared_worker_redir-other': {
+ id: "same-src-shared_worker_redir-other",
+ file: SHARED_WORKER_REDIRECT_TEST_FILE,
+ result : "blocked",
+ redir: "other",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'star-src-shared_worker_redir-same': {
+ id: "star-src-shared_worker_redir-same",
+ file: SHARED_WORKER_REDIRECT_TEST_FILE,
+ redir: "same",
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src *"
+ },
+ 'other-src-shared_worker_redir-same': {
+ id: "other-src-shared_worker_redir-same",
+ file: SHARED_WORKER_REDIRECT_TEST_FILE,
+ redir: "same",
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'; child-src https://www.example.org"
+ },
+ };
+
+ finished = {};
+
+ function recvMessage(ev) {
+ is(ev.data.message, tests[ev.data.id].result, "CSP child-src worker test " + ev.data.id);
+ finished[ev.data.id] = ev.data.message;
+
+ if (Object.keys(finished).length == Object.keys(tests).length) {
+ window.removeEventListener('message', recvMessage);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('message', recvMessage, false);
+
+ function loadNextTest() {
+ for (item in tests) {
+ test = tests[item];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + test.file);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(test.policy);
+ // add whether redirect is to same or different
+ src += "&redir=" + escape(test.policy);
+ // add our identifier
+ src += "#" + escape(test.id);
+
+ content = document.getElementById('content');
+ testframe = document.createElement("iframe");
+ testframe.setAttribute('id', test.id);
+ content.appendChild(testframe);
+ testframe.src = src;
+ }
+ }
+
+ // start running the tests
+ loadNextTest();
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/test_child-src_worker.html b/dom/security/test/csp/test_child-src_worker.html
new file mode 100644
index 000000000..7dcbd03f6
--- /dev/null
+++ b/dom/security/test/csp/test_child-src_worker.html
@@ -0,0 +1,148 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ </div>
+
+ <script class="testbody" type="text/javascript">
+ /*
+ * Description of the test:
+ * We load a page with a given CSP and verify that child frames and workers are correctly
+ * evaluated through the "child-src" directive.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var WORKER_TEST_FILE = "file_child-src_worker.html";
+ var SERVICE_WORKER_TEST_FILE = "file_child-src_service_worker.html";
+ var SHARED_WORKER_TEST_FILE = "file_child-src_shared_worker.html";
+
+ var tests = {
+ 'same-src-worker': {
+ id: "same-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'same-src-service_worker': {
+ id: "same-src-service_worker",
+ file: SERVICE_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'same-src-shared_worker': {
+ id: "same-src-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src http://mochi.test:8888"
+ },
+ 'star-src-worker': {
+ id: "star-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'star-src-service_worker': {
+ id: "star-src-service_worker",
+ file: SERVICE_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'star-src-shared_worker': {
+ id: "star-src-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'other-src-worker': {
+ id: "other-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src https://www.example.org"
+ },
+ 'other-src-service_worker': {
+ id: "other-src-service_worker",
+ file: SERVICE_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src https://www.example.org"
+ },
+ 'other-src-shared_worker': {
+ id: "other-src-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src https://www.example.org"
+ },
+ 'script-src-worker': {
+ id: "script-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ },
+ 'script-src-service_worker': {
+ id: "script-src-service_worker",
+ file: SERVICE_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ },
+ 'script-src-self-shared_worker': {
+ id: "script-src-self-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'self' 'unsafe-inline'"
+ },
+ };
+
+ finished = {};
+
+ function recvMessage(ev) {
+ is(ev.data.message, tests[ev.data.id].result, "CSP child-src worker test " + ev.data.id);
+ finished[ev.data.id] = ev.data.message;
+
+ if (Object.keys(finished).length == Object.keys(tests).length) {
+ window.removeEventListener('message', recvMessage);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('message', recvMessage, false);
+
+ function loadNextTest() {
+ for (item in tests) {
+ test = tests[item];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + test.file);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(test.policy);
+ // add our identifier
+ src += "#" + escape(test.id);
+
+ content = document.getElementById('content');
+ testframe = document.createElement("iframe");
+ testframe.setAttribute('id', test.id);
+ content.appendChild(testframe);
+ testframe.src = src;
+ }
+ }
+
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true]
+ ]}, loadNextTest);
+ };
+
+ // start running the tests
+ //loadNextTest();
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/test_child-src_worker_data.html b/dom/security/test/csp/test_child-src_worker_data.html
new file mode 100644
index 000000000..089d32dbe
--- /dev/null
+++ b/dom/security/test/csp/test_child-src_worker_data.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Bug 1045891</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ </div>
+
+ <script class="testbody" type="text/javascript">
+ /*
+ * Description of the test:
+ * We load a page with a given CSP and verify that child frames and workers are correctly
+ * evaluated through the "child-src" directive.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var WORKER_TEST_FILE = "file_child-src_worker_data.html";
+ var SHARED_WORKER_TEST_FILE = "file_child-src_shared_worker_data.html";
+
+ var tests = {
+ 'same-src-worker-no-data': {
+ id: "same-src-worker-no-data",
+ file: WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'self'"
+ },
+ 'same-src-worker': {
+ id: "same-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'self' data:"
+ },
+ 'same-src-shared_worker-no-data': {
+ id: "same-src-shared_worker-no-data",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'self'"
+ },
+ 'same-src-shared_worker': {
+ id: "same-src-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src 'self' data:"
+ },
+ 'star-src-worker': {
+ id: "star-src-worker",
+ file: WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src * data:"
+ },
+ 'star-src-worker-no-data': {
+ id: "star-src-worker-no-data",
+ file: WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'star-src-shared_worker-no-data': {
+ id: "star-src-shared_worker-no-data",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src *"
+ },
+ 'star-src-shared_worker': {
+ id: "star-src-shared_worker",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src * data:"
+ },
+ 'other-src-worker-no-data': {
+ id: "other-src-worker-no-data",
+ file: WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src https://www.example.org"
+ },
+ 'other-src-shared_worker-no-data': {
+ id: "other-src-shared_worker-no-data",
+ file: SHARED_WORKER_TEST_FILE,
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; child-src https://www.example.org"
+ },
+ };
+
+ finished = {};
+
+ function recvMessage(ev) {
+ is(ev.data.message, tests[ev.data.id].result, "CSP child-src worker test " + ev.data.id);
+ finished[ev.data.id] = ev.data.message;
+
+ if (Object.keys(finished).length == Object.keys(tests).length) {
+ window.removeEventListener('message', recvMessage);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('message', recvMessage, false);
+
+ function loadNextTest() {
+ for (item in tests) {
+ test = tests[item];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + test.file);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(test.policy);
+ // add our identifier
+ src += "#" + escape(test.id);
+
+ content = document.getElementById('content');
+ testframe = document.createElement("iframe");
+ testframe.setAttribute('id', test.id);
+ content.appendChild(testframe);
+ testframe.src = src;
+ }
+ }
+
+ // start running the tests
+ loadNextTest();
+ </script>
+ </body>
+</html>
diff --git a/dom/security/test/csp/test_connect-src.html b/dom/security/test/csp/test_connect-src.html
new file mode 100644
index 000000000..5e42d9838
--- /dev/null
+++ b/dom/security/test/csp/test_connect-src.html
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1031530 and Bug 1139667 - Test mapping of XMLHttpRequest and fetch() to connect-src</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page with a given CSP and verify that XMLHttpRequests and fetches are correctly
+ * evaluated through the "connect-src" directive. All XMLHttpRequests are served
+ * using http://mochi.test:8888, which allows the requests to succeed for the first
+ * two policies and to fail for the last policy. Please note that we have to add
+ * 'unsafe-inline' so we can run the JS test code in file_connect-src.html.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ {
+ file: "file_connect-src.html",
+ result : "allowed",
+ policy : "default-src 'none' script-src 'unsafe-inline'; connect-src http://mochi.test:8888"
+ },
+ {
+ file: "file_connect-src.html",
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src *"
+ },
+ {
+ file: "file_connect-src.html",
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src http://www.example.com"
+ },
+ {
+ file: "file_connect-src-fetch.html",
+ result : "allowed",
+ policy : "default-src 'none' script-src 'unsafe-inline'; connect-src http://mochi.test:8888"
+ },
+ {
+ file: "file_connect-src-fetch.html",
+ result : "allowed",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src *"
+ },
+ {
+ file: "file_connect-src-fetch.html",
+ result : "blocked",
+ policy : "default-src 'none'; script-src 'unsafe-inline'; connect-src http://www.example.com"
+ }
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function checkResult(aResult) {
+ is(aResult, tests[counter].result, "should be " + tests[counter].result + " in test " + counter + "!");
+ loadNextTest();
+}
+
+// We use the examiner to identify requests that hit the wire and requests
+// that are blocked by CSP and bubble up the result to the including iframe
+// document (parent).
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // making sure we do not bubble a result for something other
+ // then the request in question.
+ if (!data.includes("file_testserver.sjs?foo")) {
+ return;
+ }
+ checkResult("allowed");
+ }
+
+ if (topic === "csp-on-violate-policy") {
+ // making sure we do not bubble a result for something other
+ // then the request in question.
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+
+ if (!asciiSpec.includes("file_testserver.sjs?foo")) {
+ return;
+ }
+ checkResult("blocked");
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.ConnectSrcExaminer = new examiner();
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ window.ConnectSrcExaminer.remove();
+ SimpleTest.finish();
+ return;
+ }
+
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + tests[counter].file);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(tests[counter].policy);
+
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_docwrite_meta.html b/dom/security/test/csp/test_docwrite_meta.html
new file mode 100644
index 000000000..26794199a
--- /dev/null
+++ b/dom/security/test/csp/test_docwrite_meta.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 663570 - Implement Content Security Policy via meta tag</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe style="width:100%;" id="writemetacspframe"></iframe>
+<iframe style="width:100%;" id="commentmetacspframe"></iframe>
+
+
+<script class="testbody" type="text/javascript">
+/* Description of the test:
+ * We load two frames, where the first frame does doc.write(meta csp) and
+ * the second does doc.write(comment out meta csp).
+ * We make sure to reuse/invalidate preloads depending on the policy.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var writemetacspframe = document.getElementById("writemetacspframe");
+var commentmetacspframe = document.getElementById("commentmetacspframe");
+var seenResults = 0;
+
+function checkTestsDone() {
+ seenResults++;
+ if (seenResults < 2) {
+ return;
+ }
+ SimpleTest.finish();
+}
+
+// document.write(<meta csp ...>) should block resources from being included in the doc
+function checkResultsBlocked() {
+ writemetacspframe.removeEventListener('load', checkResultsBlocked, false);
+
+ // stylesheet: default background color within FF is transparent
+ var bgcolor = window.getComputedStyle(writemetacspframe.contentDocument.body)
+ .getPropertyValue("background-color");
+ is(bgcolor, "transparent", "inital background value in FF should be 'transparent'");
+
+ // image: make sure image is blocked
+ var img = writemetacspframe.contentDocument.getElementById("testimage");
+ is(img.width, 0, "image widht should be 0");
+ is(img.height, 0, "image widht should be 0");
+
+ // script: make sure defined variable in external script is undefined
+ is(writemetacspframe.contentDocument.myMetaCSPScript, undefined, "myMetaCSPScript should be 'undefined'");
+
+ checkTestsDone();
+}
+
+// document.write(<--) to comment out meta csp should allow resources to be loaded
+// after the preload failed
+function checkResultsAllowed() {
+ commentmetacspframe.removeEventListener('load', checkResultsAllowed, false);
+
+ // stylesheet: should be applied; bgcolor should be red
+ var bgcolor = window.getComputedStyle(commentmetacspframe.contentDocument.body).getPropertyValue("background-color");
+ is(bgcolor, "rgb(255, 0, 0)", "background should be red/rgb(255, 0, 0)");
+
+ // image: should be completed
+ var img = commentmetacspframe.contentDocument.getElementById("testimage");
+ ok(img.complete, "image should not be loaded");
+
+ // script: defined variable in external script should be accessible
+ is(commentmetacspframe.contentDocument.myMetaCSPScript, "external-JS-loaded", "myMetaCSPScript should be 'external-JS-loaded'");
+
+ checkTestsDone();
+}
+
+// doc.write(meta csp) should should allow preloads but should block actual loads
+writemetacspframe.src = 'file_docwrite_meta.html';
+writemetacspframe.addEventListener('load', checkResultsBlocked, false);
+
+// commenting out a meta CSP should result in loaded image, script, style
+commentmetacspframe.src = 'file_doccomment_meta.html';
+commentmetacspframe.addEventListener('load', checkResultsAllowed, false);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_dual_header.html b/dom/security/test/csp/test_dual_header.html
new file mode 100644
index 000000000..6d3a35fd0
--- /dev/null
+++ b/dom/security/test/csp/test_dual_header.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1036399 - Multiple CSP policies should be combined towards an intersection</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We have two tests where each tests serves a page using two CSP policies:
+ * a) * default-src 'self'
+ * * default-src 'self' 'unsafe-inline'
+ *
+ * b) * default-src 'self' 'unsafe-inline'
+ * * default-src 'self' 'unsafe-inline'
+ *
+ * We make sure the inline script is *blocked* for test (a) but *allowed* for test (b).
+ * Multiple CSPs should be combined towards an intersection and it shouldn't be possible
+ * to open up (loosen) a CSP policy.
+ */
+
+const TESTS = [
+ { query: "tight", result: "blocked" },
+ { query: "loose", result: "allowed" }
+];
+var testCounter = -1;
+
+function ckeckResult() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', ckeckResult, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, curTest.result, "should be 'blocked'!");
+ }
+ catch (e) {
+ ok(false, "error: could not access content in div container!");
+ }
+ loadNextTest();
+}
+
+function loadNextTest() {
+ testCounter++;
+ if (testCounter >= TESTS.length) {
+ SimpleTest.finish();
+ return;
+ }
+ curTest = TESTS[testCounter];
+ var src = "file_dual_header_testserver.sjs?" + curTest.query;
+ document.getElementById("testframe").addEventListener("load", ckeckResult, false);
+ document.getElementById("testframe").src = src;
+}
+
+SimpleTest.waitForExplicitFinish();
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_evalscript.html b/dom/security/test/csp/test_evalscript.html
new file mode 100644
index 000000000..f0ec3407c
--- /dev/null
+++ b/dom/security/test/csp/test_evalscript.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy "no eval" base restriction</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
+<script class="testbody" type="text/javascript">
+
+var evalScriptsThatRan = 0;
+var evalScriptsBlocked = 0;
+var evalScriptsTotal = 17;
+
+// called by scripts that run
+var scriptRan = function(shouldrun, testname, data) {
+ evalScriptsThatRan++;
+ ok(shouldrun, 'EVAL SCRIPT RAN: ' + testname + '(' + data + ')');
+ checkTestResults();
+}
+
+// called when a script is blocked
+var scriptBlocked = function(shouldrun, testname, data) {
+ evalScriptsBlocked++;
+ ok(!shouldrun, 'EVAL SCRIPT BLOCKED: ' + testname + '(' + data + ')');
+ checkTestResults();
+}
+
+var verifyZeroRetVal = function(val, testname) {
+ ok(val === 0, 'RETURN VALUE SHOULD BE ZERO, was ' + val + ': ' + testname);
+}
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+ // if any test is incomplete, keep waiting
+ if (evalScriptsTotal - evalScriptsBlocked - evalScriptsThatRan > 0)
+ return;
+
+ // ... otherwise, finish
+ SimpleTest.finish();
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_evalscript_main.html';
+document.getElementById('cspframe2').src = 'file_evalscript_main_allowed.html';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_fontloader.html b/dom/security/test/csp/test_fontloader.html
new file mode 100644
index 000000000..cdb177f2a
--- /dev/null
+++ b/dom/security/test/csp/test_fontloader.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1122236 - CSP: Implement block-all-mixed-content</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <!-- Including WindowSnapshot.js so we can take screenshots of containers !-->
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="setupTests()">
+<iframe style="width:100%;" id="baselineframe"></iframe>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the tests:
+ * We load a baselineFrame and compare the testFrame using
+ * compareSnapshots whether the font got loaded or blocked.
+ * Test 1: Use font-src 'none' so font gets blocked
+ * Test 2: Use font-src * so font gets loaded
+ * Test 3: Use no csp so font gets loaded
+ * Test 4: Use font-src 'none' so font gets blocked
+ * Makes sure the cache gets invalidated.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+const BASE_URI = "https://example.com/tests/dom/security/test/csp/";
+
+const tests = [
+ { // test 1
+ query: "csp-block",
+ expected: true, // frames should be equal since font is *not* allowed to load
+ description: "font should be blocked by csp (csp-block)"
+ },
+ { // test 2
+ query: "csp-allow",
+ expected: false, // frames should *not* be equal since font is loaded
+ description: "font should load and apply (csp-allow)"
+ },
+ { // test 3
+ query: "no-csp",
+ expected: false, // frames should *not* be equals since font is loaded
+ description: "font should load and apply (no-csp)"
+ },
+ { // test 4
+ query: "csp-block",
+ expected: true, // frames should be equal since font is *not* allowed to load
+ description: "font should be blocked by csp (csp-block) [apply csp to cache]"
+ }
+];
+
+var curTest;
+var counter = -1;
+var baselineframe = document.getElementById("baselineframe");
+var testframe = document.getElementById("testframe");
+
+function checkResult() {
+ testframe.removeEventListener('load', checkResult, false);
+ try {
+ ok(compareSnapshots(snapshotWindow(baselineframe.contentWindow),
+ snapshotWindow(testframe.contentWindow),
+ curTest.expected)[0],
+ curTest.description);
+ } catch(err) {
+ ok(false, "error: " + err.message);
+ }
+ loadNextTest();
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+ curTest = tests[counter];
+ testframe.addEventListener("load", checkResult, false);
+ testframe.src = BASE_URI + "file_fontloader.sjs?" + curTest.query;
+}
+
+// once the baselineframe is loaded we can start running tests
+function startTests() {
+ baselineframe.removeEventListener('load', startTests, false);
+ loadNextTest();
+}
+
+// make sure the main page is loaded before we start the test
+function setupTests() {
+ baselineframe.addEventListener("load", startTests, false);
+ baselineframe.src = BASE_URI + "file_fontloader.sjs?baseline";
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_form-action.html b/dom/security/test/csp/test_form-action.html
new file mode 100644
index 000000000..b909ca701
--- /dev/null
+++ b/dom/security/test/csp/test_form-action.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 529697 - Test mapping of form submission to form-action</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page with a given CSP and verify that form submissions are correctly
+ * evaluated through the "form-action" directive.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ {
+ page : "file_form-action.html",
+ result : "allowed",
+ policy : "form-action 'self'"
+ },
+ {
+ page : "file_form-action.html",
+ result : "blocked",
+ policy : "form-action 'none'"
+ }
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function checkResult(aResult) {
+ is(aResult, tests[counter].result, "should be " + tests[counter].result + " in test " + counter + "!");
+ loadNextTest();
+}
+
+// We use the examiner to identify requests that hit the wire and requests
+// that are blocked by CSP and bubble up the result to the including iframe
+// document (parent).
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // making sure we do not bubble a result for something other
+ // then the request in question.
+ if (!data.includes("submit-form")) {
+ return;
+ }
+ checkResult("allowed");
+ }
+
+ if (topic === "csp-on-violate-policy") {
+ // making sure we do not bubble a result for something other
+ // then the request in question.
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+ if (!asciiSpec.includes("submit-form")) {
+ return;
+ }
+ checkResult("blocked");
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.FormActionExaminer = new examiner();
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ window.FormActionExaminer.remove();
+ SimpleTest.finish();
+ return;
+ }
+
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/" + tests[counter].page);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(tests[counter].policy);
+
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_form_action_blocks_url.html b/dom/security/test/csp/test_form_action_blocks_url.html
new file mode 100644
index 000000000..ef5c8d9b4
--- /dev/null
+++ b/dom/security/test/csp/test_form_action_blocks_url.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bug 1251043 - Test form-action blocks URL</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * 1) Let's load a form into an iframe which uses a CSP of: form-action 'none';
+ * 2) Let's hit the submit button and make sure the form is not submitted.
+ *
+ * Since a blocked form submission does not fire any event handler, we have to
+ * use timeout triggered function that verifies that the form didn't get submitted.
+ */
+
+SimpleTest.requestFlakyTimeout(
+ "Form submission blocked by CSP does not fire any events " +
+ "hence we have to check back after 300ms to make sure the form " +
+ "is not submitted");
+SimpleTest.waitForExplicitFinish();
+
+const FORM_SUBMITTED = "form submission succeeded";
+var timeOutId;
+var testframe = document.getElementById("testframe");
+
+// In case the form gets submitted, the test would receive an 'load'
+// event and would trigger the test to fail early.
+function logFormSubmittedError() {
+ clearTimeout(timeOutId);
+ testframe.removeEventListener('load', logFormSubmittedError, false);
+ ok(false, "form submission should be blocked");
+ SimpleTest.finish();
+}
+
+// After 300ms we verify the form did not get submitted.
+function verifyFormNotSubmitted() {
+ clearTimeout(timeOutId);
+ var frameContent = testframe.contentWindow.document.body.innerHTML;
+ isnot(frameContent.indexOf("CONTROL-TEXT"), -1,
+ "form should not be submitted and still contain the control text");
+ SimpleTest.finish();
+}
+
+function submitForm() {
+ // Part 1: The form has loaded in the testframe
+ // unregister the current event handler
+ testframe.removeEventListener('load', submitForm, false);
+
+ // Part 2: Register a new load event handler. In case the
+ // form gets submitted, this load event fires and we can
+ // fail the test right away.
+ testframe.addEventListener("load", logFormSubmittedError, false);
+
+ // Part 3: Since blocking the form does not throw any kind of error;
+ // Firefox just logs the CSP error to the console we have to register
+ // this timeOut function which then verifies that the form didn't
+ // get submitted.
+ timeOutId = setTimeout(verifyFormNotSubmitted, 300);
+
+ // Part 4: We are ready, let's hit the submit button of the form.
+ var submitButton = testframe.contentWindow.document.getElementById('submitButton');
+ submitButton.click();
+}
+
+testframe.addEventListener("load", submitForm, false);
+testframe.src = "file_form_action_server.sjs?loadframe";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_frameancestors.html b/dom/security/test/csp/test_frameancestors.html
new file mode 100644
index 000000000..8ccbc8156
--- /dev/null
+++ b/dom/security/test/csp/test_frameancestors.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy Frame Ancestors directive</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+var framesThatShouldLoad = {
+ aa_allow: -1, /* innermost frame allows a *
+ //aa_block: -1, /* innermost frame denies a */
+ ab_allow: -1, /* innermost frame allows a */
+ //ab_block: -1, /* innermost frame denies a */
+ aba_allow: -1, /* innermost frame allows b,a */
+ //aba_block: -1, /* innermost frame denies b */
+ //aba2_block: -1, /* innermost frame denies a */
+ abb_allow: -1, /* innermost frame allows b,a */
+ //abb_block: -1, /* innermost frame denies b */
+ //abb2_block: -1, /* innermost frame denies a */
+};
+
+// we normally expect _6_ violations (6 test cases that cause blocks), but many
+// of the cases cause violations due to the // origin of the test harness (same
+// as 'a'). When the violation is cross-origin, the URI passed to the observers
+// is null (see bug 846978). This means we can't tell if it's part of the test
+// case or if it is the test harness frame (also origin 'a').
+// As a result, we'll get an extra violation for the following cases:
+// ab_block "frame-ancestors 'none'" (outer frame and test harness)
+// aba2_block "frame-ancestors b" (outer frame and test harness)
+// abb2_block "frame-ancestors b" (outer frame and test harness)
+//
+// and while we can detect the test harness check for this one case since
+// the violations are not cross-origin and we get the URI:
+// aba2_block "frame-ancestors b" (outer frame and test harness)
+//
+// we can't for these other ones:
+// ab_block "frame-ancestors 'none'" (outer frame and test harness)
+// abb2_block "frame-ancestors b" (outer frame and test harness)
+//
+// so that results in 2 extra violation notifications due to the test harness.
+// expected = 6, total = 8
+//
+// Number of tests that pass for this file should be 12 (8 violations 4 loads)
+var expectedViolationsLeft = 8;
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ // subject should be an nsURI... though could be null since CSP
+ // prohibits cross-origin URI reporting during frame ancestors checks.
+ if (subject && !SpecialPowers.can_QI(subject))
+ return;
+
+ var asciiSpec = subject;
+
+ try {
+ asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+
+ // skip checks on the test harness -- can't do this skipping for
+ // cross-origin blocking since the observer doesn't get the URI. This
+ // can cause this test to over-succeed (but only in specific cases).
+ if (asciiSpec.includes("test_frameancestors.html")) {
+ return;
+ }
+ } catch (ex) {
+ // was not an nsIURI, so it was probably a cross-origin report.
+ }
+
+
+ if (topic === "csp-on-violate-policy") {
+ //these were blocked... record that they were blocked
+ window.frameBlocked(asciiSpec, data);
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+// called when a frame is loaded
+// -- if it's not enumerated above, it should not load!
+var frameLoaded = function(testname, uri) {
+ //test already complete.... forget it... remember the first result.
+ if (window.framesThatShouldLoad[testname] != -1)
+ return;
+
+ if (typeof window.framesThatShouldLoad[testname] === 'undefined') {
+ // uh-oh, we're not expecting this frame to load!
+ ok(false, testname + ' framed site should not have loaded: ' + uri);
+ } else {
+ framesThatShouldLoad[testname] = true;
+ ok(true, testname + ' framed site when allowed by csp (' + uri + ')');
+ }
+ checkTestResults();
+}
+
+// called when a frame is blocked
+// -- we can't determine *which* frame was blocked, but at least we can count them
+var frameBlocked = function(uri, policy) {
+ ok(true, 'a CSP policy blocked frame from being loaded: ' + policy);
+ expectedViolationsLeft--;
+ checkTestResults();
+}
+
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+ // if any test is incomplete, keep waiting
+ for (var v in framesThatShouldLoad)
+ if(window.framesThatShouldLoad[v] == -1)
+ return;
+
+ if (window.expectedViolationsLeft > 0)
+ return;
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+ if (event.data.call && event.data.call == 'frameLoaded')
+ frameLoaded(event.data.testname, event.data.uri);
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_frameancestors_main.html';
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_hash_source.html b/dom/security/test/csp/test_hash_source.html
new file mode 100644
index 000000000..6dde11dac
--- /dev/null
+++ b/dom/security/test/csp/test_hash_source.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test CSP 1.1 hash-source for inline scripts and styles</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="visibility:hidden">
+ <iframe style="width:100%;" id='cspframe'></iframe>
+</div>
+<script class="testbody" type="text/javascript">
+
+function cleanup() {
+ // finish the tests
+ SimpleTest.finish();
+}
+
+function checkInline () {
+ var cspframe = document.getElementById('cspframe').contentDocument;
+
+ var inlineScriptTests = {
+ 'inline-script-valid-hash': {
+ shouldBe: 'allowed',
+ message: 'Inline script with valid hash should be allowed'
+ },
+ 'inline-script-invalid-hash': {
+ shouldBe: 'blocked',
+ message: 'Inline script with invalid hash should be blocked'
+ },
+ 'inline-script-invalid-hash-valid-nonce': {
+ shouldBe: 'allowed',
+ message: 'Inline script with invalid hash and valid nonce should be allowed'
+ },
+ 'inline-script-valid-hash-invalid-nonce': {
+ shouldBe: 'allowed',
+ message: 'Inline script with valid hash and invalid nonce should be allowed'
+ },
+ 'inline-script-invalid-hash-invalid-nonce': {
+ shouldBe: 'blocked',
+ message: 'Inline script with invalid hash and invalid nonce should be blocked'
+ },
+ 'inline-script-valid-sha512-hash': {
+ shouldBe: 'allowed',
+ message: 'Inline script with a valid sha512 hash should be allowed'
+ },
+ 'inline-script-valid-sha384-hash': {
+ shouldBe: 'allowed',
+ message: 'Inline script with a valid sha384 hash should be allowed'
+ },
+ 'inline-script-valid-sha1-hash': {
+ shouldBe: 'blocked',
+ message: 'Inline script with a valid sha1 hash should be blocked, because sha1 is not a valid hash function'
+ },
+ 'inline-script-valid-md5-hash': {
+ shouldBe: 'blocked',
+ message: 'Inline script with a valid md5 hash should be blocked, because md5 is not a valid hash function'
+ }
+ }
+
+ for (testId in inlineScriptTests) {
+ var test = inlineScriptTests[testId];
+ is(cspframe.getElementById(testId).innerHTML, test.shouldBe, test.message);
+ }
+
+ // Inline style tries to change an element's color to green. If blocked, the
+ // element's color will be the default black.
+ var green = "rgb(0, 128, 0)";
+ var black = "rgb(0, 0, 0)";
+
+ var getElementColorById = function (id) {
+ return window.getComputedStyle(cspframe.getElementById(id), null).color;
+ };
+
+ var inlineStyleTests = {
+ 'inline-style-valid-hash': {
+ shouldBe: green,
+ message: 'Inline style with valid hash should be allowed'
+ },
+ 'inline-style-invalid-hash': {
+ shouldBe: black,
+ message: 'Inline style with invalid hash should be blocked'
+ },
+ 'inline-style-invalid-hash-valid-nonce': {
+ shouldBe: green,
+ message: 'Inline style with invalid hash and valid nonce should be allowed'
+ },
+ 'inline-style-valid-hash-invalid-nonce': {
+ shouldBe: green,
+ message: 'Inline style with valid hash and invalid nonce should be allowed'
+ },
+ 'inline-style-invalid-hash-invalid-nonce' : {
+ shouldBe: black,
+ message: 'Inline style with invalid hash and invalid nonce should be blocked'
+ },
+ 'inline-style-valid-sha512-hash': {
+ shouldBe: green,
+ message: 'Inline style with a valid sha512 hash should be allowed'
+ },
+ 'inline-style-valid-sha384-hash': {
+ shouldBe: green,
+ message: 'Inline style with a valid sha384 hash should be allowed'
+ },
+ 'inline-style-valid-sha1-hash': {
+ shouldBe: black,
+ message: 'Inline style with a valid sha1 hash should be blocked, because sha1 is not a valid hash function'
+ },
+ 'inline-style-valid-md5-hash': {
+ shouldBe: black,
+ message: 'Inline style with a valid md5 hash should be blocked, because md5 is not a valid hash function'
+ }
+ }
+
+ for (testId in inlineStyleTests) {
+ var test = inlineStyleTests[testId];
+ is(getElementColorById(testId), test.shouldBe, test.message);
+ }
+
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_hash_source.html';
+document.getElementById('cspframe').addEventListener('load', checkInline, false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_iframe_sandbox.html b/dom/security/test/csp/test_iframe_sandbox.html
new file mode 100644
index 000000000..f79f94135
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox.html
@@ -0,0 +1,239 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=671389
+Bug 671389 - Implement CSP sandbox directive
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 671389</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Check if two sandbox flags are the same, ignoring case-sensitivity.
+ // getSandboxFlags returns a list of sandbox flags (if any) or
+ // null if the flag is not set.
+ // This function checks if two flags are the same, i.e., they're
+ // either not set or have the same flags.
+ function eqFlags(a, b) {
+ if (a === null && b === null) { return true; }
+ if (a === null || b === null) { return false; }
+ if (a.length !== b.length) { return false; }
+ var a_sorted = a.map(function(e) { return e.toLowerCase(); }).sort();
+ var b_sorted = b.map(function(e) { return e.toLowerCase(); }).sort();
+ for (var i in a_sorted) {
+ if (a_sorted[i] !== b_sorted[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Get the sandbox flags of document doc.
+ // If the flag is not set sandboxFlagsAsString returns null,
+ // this function also returns null.
+ // If the flag is set it may have some flags; in this case
+ // this function returns the (potentially empty) list of flags.
+ function getSandboxFlags(doc) {
+ var flags = doc.sandboxFlagsAsString;
+ if (flags === null) { return null; }
+ return flags? flags.split(" "):[];
+ }
+
+ // Constructor for a CSP sandbox flags test. The constructor
+ // expectes a description 'desc' and set of options 'opts':
+ // - sandboxAttribute: [null] or string corresponding to the iframe sandbox attributes
+ // - csp: [null] or string corresponding to the CSP sandbox flags
+ // - cspReportOnly: [null] or string corresponding to the CSP report-only sandbox flags
+ // - file: [null] or string corresponding to file the server should serve
+ // Above, we use [brackets] to denote default values.
+ function CSPFlagsTest(desc, opts) {
+ function ifundef(x, v) {
+ return (x !== undefined) ? x : v;
+ }
+
+ function intersect(as, bs) { // Intersect two csp attributes:
+ as = as === null ? null
+ : as.split(' ').filter(function(x) { return !!x; });
+ bs = bs === null ? null
+ : bs.split(' ').filter(function(x) { return !!x; });
+
+ if (as === null) { return bs; }
+ if (bs === null) { return as; }
+
+ var cs = [];
+ as.forEach(function(a) {
+ if (a && bs.indexOf(a) != -1)
+ cs.push(a);
+ });
+ return cs;
+ }
+
+ this.desc = desc || "Untitled test";
+ this.attr = ifundef(opts.sandboxAttribute, null);
+ this.csp = ifundef(opts.csp, null);
+ this.cspRO = ifundef(opts.cspReportOnly, null);
+ this.file = ifundef(opts.file, null);
+ this.expected = intersect(this.attr, this.csp);
+ }
+
+ // Return function that checks that the actual flags are the same as the
+ // expected flags
+ CSPFlagsTest.prototype.checkFlags = function(iframe) {
+ var this_ = this;
+ return function() {
+ try {
+ var actual = getSandboxFlags(SpecialPowers.wrap(iframe).contentDocument);
+ ok(eqFlags(actual, this_.expected),
+ this_.desc, 'expected: "' + this_.expected + '", got: "' + actual + '"');
+ } catch (e) {
+ ok(false, this_.desc, 'expected: "' + this_.expected + '", failed with: "' + e + '"');
+ }
+ runNextTest();
+ };
+ };
+
+ // Set the iframe src and sandbox attribute
+ CSPFlagsTest.prototype.runTest = function () {
+ var iframe = document.createElement('iframe');
+ document.getElementById("content").appendChild(iframe);
+ iframe.onload = this.checkFlags(iframe);
+
+ // set sandbox attribute
+ if (this.attr === null) {
+ iframe.removeAttribute('sandbox');
+ } else {
+ iframe.sandbox = this.attr;
+ }
+
+ // set query string
+ var src = 'http://mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs';
+
+ var delim = '?';
+
+ if (this.csp !== null) {
+ src += delim + 'csp=' + escape('sandbox ' + this.csp);
+ delim = '&';
+ }
+
+ if (this.cspRO !== null) {
+ src += delim + 'cspRO=' + escape('sandbox ' + this.cspRO);
+ delim = '&';
+ }
+
+ if (this.file !== null) {
+ src += delim + 'file=' + escape(this.file);
+ delim = '&';
+ }
+
+ iframe.src = src;
+ iframe.width = iframe.height = 10;
+
+ }
+
+ testCases = [
+ {
+ desc: "Test 1: Header should not override attribute",
+ sandboxAttribute: "",
+ csp: "allow-forms aLLOw-POinter-lock alLOW-popups aLLOW-SAME-ORIGin ALLOW-SCRIPTS allow-top-navigation"
+ },
+ {
+ desc: "Test 2: Attribute should not override header",
+ sandboxAttribute: "sandbox allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation",
+ csp: ""
+ },
+ {
+ desc: "Test 3: Header and attribute intersect",
+ sandboxAttribute: "allow-same-origin allow-scripts",
+ csp: "allow-forms allow-same-origin allow-scripts"
+ },
+ {
+ desc: "Test 4: CSP sandbox sets the right flags (pt 1)",
+ csp: "alLOW-FORms ALLOW-pointer-lock allow-popups allow-same-origin allow-scripts ALLOW-TOP-NAVIGation"
+ },
+ {
+ desc: "Test 5: CSP sandbox sets the right flags (pt 2)",
+ csp: "allow-same-origin allow-TOP-navigation"
+ },
+ {
+ desc: "Test 6: CSP sandbox sets the right flags (pt 3)",
+ csp: "allow-FORMS ALLOW-scripts"
+ },
+ {
+ desc: "Test 7: CSP sandbox sets the right flags (pt 4)",
+ csp: ""
+ },
+ {
+ desc: "Test 8: CSP sandbox sets the right flags (pt 5)",
+ csp: null
+ },
+ {
+ desc: "Test 9: Read-only header should not override attribute",
+ sandboxAttribute: "",
+ cspReportOnly: "allow-forms ALLOW-pointer-lock allow-POPUPS allow-same-origin ALLOW-scripts allow-top-NAVIGATION"
+ },
+ {
+ desc: "Test 10: Read-only header should not override CSP header",
+ csp: "allow-forms allow-scripts",
+ cspReportOnly: "allow-forms aLlOw-PoInTeR-lOcK aLLow-pOPupS aLLoW-SaME-oRIgIN alLow-scripts allow-tOp-navigation"
+ },
+ {
+ desc: "Test 11: Read-only header should not override attribute or CSP header",
+ sandboxAttribute: "allow-same-origin allow-scripts",
+ csp: "allow-forms allow-same-origin allow-scripts",
+ cspReportOnly: "allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation"
+ },
+ {
+ desc: "Test 12: CSP sandbox not affected by document.write()",
+ csp: "allow-scripts",
+ file: 'tests/dom/security/test/csp/file_iframe_sandbox_document_write.html'
+ },
+ ].map(function(t) { return (new CSPFlagsTest(t.desc,t)); });
+
+
+ var testCaseIndex = 0;
+
+ // Track ok messages from iframes
+ var childMessages = 0;
+ var totalChildMessages = 1;
+
+
+ // Check to see if we ran all the tests and received all messges
+ // from child iframes. If so, finish.
+ function tryFinish() {
+ if (testCaseIndex === testCases.length && childMessages === totalChildMessages){
+ SimpleTest.finish();
+ }
+ }
+
+ function runNextTest() {
+
+ tryFinish();
+
+ if (testCaseIndex < testCases.length) {
+ testCases[testCaseIndex].runTest();
+ testCaseIndex++;
+ }
+ }
+
+ function receiveMessage(event) {
+ ok(event.data.ok, event.data.desc);
+ childMessages++;
+ tryFinish();
+ }
+
+ window.addEventListener("message", receiveMessage, false);
+
+ addLoadEvent(runNextTest);
+</script>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=671389">Mozilla Bug 671389</a> - Implement CSP sandbox directive
+ <p id="display"></p>
+ <div id="content">
+ </div>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_iframe_sandbox_srcdoc.html b/dom/security/test/csp/test_iframe_sandbox_srcdoc.html
new file mode 100644
index 000000000..53beafcac
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox_srcdoc.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1073952 - CSP should restrict scripts in srcdoc iframe even if sandboxed</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display">Bug 1073952</p>
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+
+ if(topic === "csp-on-violate-policy") {
+ var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
+ do_QueryInterface(subject, "nsISupportsCString"), "data");
+ // the violation subject for inline script violations is unfortunately vague,
+ // all we can do is match the string.
+ if (!violationString.includes("Inline Script")) {
+ return
+ }
+ ok(true, "CSP inherited into sandboxed srcdoc iframe, script blocked.");
+ window.testFinished();
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+window.examiner = new examiner();
+
+function testFinished() {
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+addEventListener("message", function(e) {
+ ok(false, "We should not execute JS in srcdoc iframe.");
+ window.testFinished();
+})
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_iframe_sandbox_srcdoc.html';
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_iframe_sandbox_top_1.html b/dom/security/test/csp/test_iframe_sandbox_top_1.html
new file mode 100644
index 000000000..d9ba71824
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox_top_1.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=671389
+Bug 671389 - Implement CSP sandbox directive
+
+Tests CSP sandbox attribute on top-level page.
+
+Minimal flags: allow-same-origin allow-scripts:
+Since we need to load the SimpleTest files, we have to set the
+allow-same-origin flag. Additionally, we set the allow-scripts flag
+since we need JS to check the flags.
+
+Though not necessary, for this test we also set the allow-forms flag.
+We may later wish to extend the testing suite with sandbox_csp_top_*
+tests that set different permutations of the flags.
+
+CSP header: Content-Security-Policy: sandbox allow-forms allow-scripts allow-same-origin
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 671389</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// Check if two sandbox flags are the same.
+// getSandboxFlags returns a list of sandbox flags (if any) or
+// null if the flag is not set.
+// This function checks if two flags are the same, i.e., they're
+// either not set or have the same flags.
+function eqFlags(a, b) {
+ if (a === null && b === null) { return true; }
+ if (a === null || b === null) { return false; }
+ if (a.length !== b.length) { return false; }
+ var a_sorted = a.sort();
+ var b_sorted = b.sort();
+ for (var i in a_sorted) {
+ if (a_sorted[i] !== b_sorted[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Get the sandbox flags of document doc.
+// If the flag is not set sandboxFlagsAsString returns null,
+// this function also returns null.
+// If the flag is set it may have some flags; in this case
+// this function returns the (potentially empty) list of flags.
+function getSandboxFlags(doc) {
+ var flags = doc.sandboxFlagsAsString;
+ if (flags === null) { return null; }
+ return flags? flags.split(" "):[];
+}
+
+function checkFlags(expected) {
+ try {
+ var flags = getSandboxFlags(SpecialPowers.wrap(document));
+ ok(eqFlags(flags, expected), name + ' expected: "' + expected + '", got: "' + flags + '"');
+ } catch (e) {
+ ok(false, name + ' expected "' + expected + ', but failed with ' + e);
+ }
+ SimpleTest.finish();
+}
+
+</script>
+
+<body onLoad='checkFlags(["allow-forms", "allow-scripts", "allow-same-origin"]);'>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=671389">Mozilla Bug 671389</a> - Implement CSP sandbox directive
+<p id="display"></p>
+<div id="content">
+ I am a top-level page sandboxed with "allow-scripts allow-forms
+ allow-same-origin".
+</div>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_iframe_sandbox_top_1.html^headers^ b/dom/security/test/csp/test_iframe_sandbox_top_1.html^headers^
new file mode 100644
index 000000000..d9cd0606e
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_sandbox_top_1.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: sAnDbOx aLLow-FOrms aLlOw-ScRiPtS ALLOW-same-origin
diff --git a/dom/security/test/csp/test_iframe_srcdoc.html b/dom/security/test/csp/test_iframe_srcdoc.html
new file mode 100644
index 000000000..95b924a5e
--- /dev/null
+++ b/dom/security/test/csp/test_iframe_srcdoc.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1073952 - Test CSP enforcement within iframe srcdoc</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * (1) We serve a site which makes use of script-allowed sandboxed iframe srcdoc
+ * and make sure that CSP applies to the nested browsing context
+ * within the iframe.
+ * [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [SCRIPT]]]
+ *
+ * (2) We serve a site which nests script within an script-allowed sandboxed
+ * iframe srcdoc within another script-allowed sandboxed iframe srcdoc and
+ * make sure that CSP applies to the nested browsing context
+ * within the iframe*s*.
+ * [PAGE WITH CSP [IFRAME SANDBOX SRCDOC [IFRAME SANDBOX SRCDOC [SCRIPT]]]]
+ *
+ * Please note that the test relies on the "csp-on-violate-policy" observer.
+ * Whenever the script within the iframe is blocked observers are notified.
+ * In turn, this renders the 'result' within tests[] unused. In case the script
+ * would execute however, the postMessageHandler would bubble up 'allowed' and
+ * the test would fail.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ // [PAGE *WITHOUT* CSP [IFRAME SRCDOC [SCRIPT]]]
+ { csp: "",
+ result: "allowed",
+ query: "simple_iframe_srcdoc",
+ desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "script-src https://test1.com",
+ result: "blocked",
+ query: "simple_iframe_srcdoc",
+ desc: "CSP should block script within script-allowed sandboxediframe srcdoc"
+ },
+ // [PAGE *WITHOUT* CSP [IFRAME SRCDOC [IFRAME SRCDOC [SCRIPT]]]]
+ { csp: "",
+ result: "allowed",
+ query: "nested_iframe_srcdoc",
+ desc: "No CSP should run script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ // [PAGE WITH CSP [IFRAME SRCDOC ]]
+ { csp: "script-src https://test2.com",
+ result: "blocked",
+ query: "nested_iframe_srcdoc",
+ desc: "CSP should block script within script-allowed sandboxed iframe srcdoc nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "",
+ result: "allowed",
+ query: "nested_iframe_srcdoc_datauri",
+ desc: "No CSP, should run script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
+ },
+ { csp: "script-src https://test3.com",
+ result: "blocked",
+ query: "nested_iframe_srcdoc_datauri",
+ desc: "CSP should block script within script-allowed sandboxed iframe src with data URL nested within another script-allowed sandboxed iframe srcdoc"
+ },
+
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ testComplete(result, tests[counter].result, tests[counter].desc);
+}
+
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "csp-on-violate-policy") {
+ var violationString = SpecialPowers.getPrivilegedProps(SpecialPowers.
+ do_QueryInterface(subject, "nsISupportsCString"), "data");
+ // the violation subject for inline script violations is unfortunately vague,
+ // all we can do is match the string.
+ if (!violationString.includes("Inline Script")) {
+ return
+ }
+ testComplete("blocked", tests[counter].result, tests[counter].desc);
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+function testComplete(result, expected, desc) {
+ is(result, expected, desc);
+ // ignore cases when we get csp violations and postMessage from the same frame.
+ var frameURL = new URL(document.getElementById("testframe").src);
+ var params = new URLSearchParams(frameURL.search);
+ var counterInFrame = params.get("counter");
+ if (counterInFrame == counter) {
+ loadNextTest();
+ }
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ var src = "file_iframe_srcdoc.sjs";
+ src += "?csp=" + escape(tests[counter].csp);
+ src += "&action=" + escape(tests[counter].query);
+ src += "&counter=" + counter;
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+window.examiner = new examiner();
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_ignore_unsafe_inline.html b/dom/security/test/csp/test_ignore_unsafe_inline.html
new file mode 100644
index 000000000..50f9cbb8d
--- /dev/null
+++ b/dom/security/test/csp/test_ignore_unsafe_inline.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1004703 - ignore 'unsafe-inline' if nonce- or hash-source specified</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We load a page that contains three scripts using different policies
+ * and make sure 'unsafe-inline' is ignored within script-src if hash-source
+ * or nonce-source is specified.
+ *
+ * The expected output of each test is a sequence of chars.
+ * E.g. the default char we expect is 'a', depending on what inline scripts
+ * are allowed to run we also expect 'b', 'c', 'd'.
+ *
+ * The test also covers the handling of multiple policies where the second
+ * policy makes use of a directive that should *not* fallback to
+ * default-src, see Bug 1198422.
+ */
+
+const POLICY_PREFIX = "default-src 'none'; script-src ";
+
+var tests = [
+ {
+ policy1: POLICY_PREFIX + "'unsafe-inline'",
+ policy2: "frame-ancestors 'self'",
+ description: "'unsafe-inline' allows all scripts to execute",
+ file: "file_ignore_unsafe_inline.html",
+ result: "abcd",
+ },
+ {
+ policy1: POLICY_PREFIX + "'unsafe-inline' 'sha256-uJXAPKP5NZxnVMZMUkDofh6a9P3UMRc1CRTevVPS/rI='",
+ policy2: "base-uri http://mochi.test",
+ description: "defining a hash should only allow one script to execute",
+ file: "file_ignore_unsafe_inline.html",
+ result: "ac",
+ },
+ {
+ policy1: POLICY_PREFIX + "'unsafe-inline' 'nonce-FooNonce'",
+ policy2: "form-action 'none'",
+ description: "defining a nonce should only allow one script to execute",
+ file: "file_ignore_unsafe_inline.html",
+ result: "ad",
+ },
+ {
+ policy1: POLICY_PREFIX + "'unsafe-inline' 'sha256-uJXAPKP5NZxnVMZMUkDofh6a9P3UMRc1CRTevVPS/rI=' 'nonce-FooNonce'",
+ policy2: "upgrade-insecure-requests",
+ description: "defining hash and nonce should allow two scripts to execute",
+ file: "file_ignore_unsafe_inline.html",
+ result: "acd",
+ },
+ {
+ policy1: POLICY_PREFIX + "'unsafe-inline' 'sha256-uJXAPKP5NZxnVMZMUkDofh6a9P3UMRc1CRTevVPS/rI=' 'nonce-FooNonce' 'unsafe-inline'",
+ policy2: "referrer origin",
+ description: "defining hash, nonce and 'unsafe-inline' twice should still only allow two scripts to execute",
+ file: "file_ignore_unsafe_inline.html",
+ result: "acd",
+ },
+ {
+ policy1: "default-src 'unsafe-inline' 'sha256-uJXAPKP5NZxnVMZMUkDofh6a9P3UMRc1CRTevVPS/rI=' 'nonce-FooNonce' ",
+ policy2: "sandbox allow-scripts allow-same-origin",
+ description: "unsafe-inline should *not* be ignored within default-src even if hash or nonce is specified",
+ file: "file_ignore_unsafe_inline.html",
+ result: "abcd",
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ document.getElementById("testframe").removeEventListener("load", test, false);
+ SimpleTest.finish();
+ return;
+ }
+
+ curTest = tests[counter++];
+ var src = "file_ignore_unsafe_inline_multiple_policies_server.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/" + curTest.file);
+
+ // append the first CSP that should be used to serve the file
+ src += "&csp1=" + escape(curTest.policy1);
+ // append the second CSP that should be used to serve the file
+ src += "&csp2=" + escape(curTest.policy2);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ // sort the characters to make sure the result is in ascending order
+ // in case handlers run out of order
+ divcontent = divcontent.split('').sort().join('');
+
+ is(divcontent, curTest.result, curTest.description);
+ }
+ catch (e) {
+ ok(false, "error: could not access content for test " + curTest.description + "!");
+ }
+ loadNextTest();
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_inlinescript.html b/dom/security/test/csp/test_inlinescript.html
new file mode 100644
index 000000000..7fd5695d5
--- /dev/null
+++ b/dom/security/test/csp/test_inlinescript.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Content Security Policy Frame Ancestors directive</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe style="width:100%;height:300px;" id='testframe'></iframe>
+
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ {
+ /* test allowed */
+ csp: "default-src 'self'; script-src 'self' 'unsafe-inline'",
+ results: ["body-onload-fired", "text-node-fired",
+ "javascript-uri-fired", "javascript-uri-anchor-fired"],
+ desc: "allow inline scripts",
+ received: 0, // counter to make sure we received all 4 reports
+ },
+ {
+ /* test blocked */
+ csp: "default-src 'self'",
+ results: ["inline-script-blocked"],
+ desc: "block inline scripts",
+ received: 0, // counter to make sure we received all 4 reports
+ }
+];
+
+var counter = 0;
+var curTest;
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic !== "csp-on-violate-policy") {
+ return;
+ }
+
+ var what = SpecialPowers.getPrivilegedProps(SpecialPowers.
+ do_QueryInterface(subject, "nsISupportsCString"), "data");
+
+ if (!what.includes("Inline Script had invalid hash") &&
+ !what.includes("Inline Scripts will not execute")) {
+ return;
+ }
+ window.checkResults("inline-script-blocked");
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+function finishTest() {
+ window.examiner.remove();
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+// Check to see if all the tests have run
+var checkResults = function(result) {
+ var index = curTest.results.indexOf(result);
+ isnot(index, -1, "should find result (" + result +") within test: " + curTest.desc);
+ if (index > -1) {
+ curTest.received += 1;
+ }
+
+ // make sure we receive all the 4 reports for the 4 inline scripts
+ if (curTest.received < 4) {
+ return;
+ }
+
+ if (counter < tests.length) {
+ loadNextTest();
+ return;
+ }
+ finishTest();
+}
+
+// a postMessage handler that is used to bubble up results from the testframe
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data);
+}
+
+function clickit() {
+ document.getElementById("testframe").removeEventListener('load', clickit, false);
+ var testframe = document.getElementById('testframe');
+ var a = testframe.contentDocument.getElementById('anchortoclick');
+ sendMouseEvent({type:'click'}, a, testframe.contentWindow);
+}
+
+function loadNextTest() {
+ curTest = tests[counter++];
+ var src = "file_testserver.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/file_inlinescript.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.csp);
+
+ document.getElementById("testframe").src = src;
+ document.getElementById("testframe").addEventListener("load", clickit, false);
+}
+
+// set up the test and go
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+loadNextTest();
+
+</script>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/test_inlinestyle.html b/dom/security/test/csp/test_inlinestyle.html
new file mode 100644
index 000000000..eadcf1e38
--- /dev/null
+++ b/dom/security/test/csp/test_inlinestyle.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Content Security Policy inline stylesheets stuff</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:100%;height:300px;" id='cspframe1'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
+<script class="testbody" type="text/javascript">
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+var done = 0;
+
+// When a CSP 1.0 compliant policy is specified we should block inline
+// styles applied by <style> element, style attribute, and SMIL <animate> and <set> tags
+// (when it's not explicitly allowed.)
+function checkStyles(evt) {
+ var cspframe = document.getElementById('cspframe1');
+ var color;
+
+ // black means the style wasn't applied. green colors are used for styles
+ //expected to be applied. A color is red if a style is erroneously applied
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('linkstylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'External Stylesheet (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('inlinestylediv'),null)['color'];
+ ok('rgb(0, 0, 0)' === color, 'Inline Style TAG (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('attrstylediv'),null)['color'];
+ ok('rgb(0, 0, 0)' === color, 'Style Attribute (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('csstextstylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'cssText (' + color + ')');
+ // SMIL tests
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('xmlTest',null))['fill'];
+ ok('rgb(0, 0, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTest',null))['fill'];
+ ok('rgb(0, 0, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
+ ok('rgb(0, 0, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssSetTestById',null))['fill'];
+ ok('rgb(0, 0, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
+
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
+
+ checkIfDone();
+}
+
+// When a CSP 1.0 compliant policy is specified we should allow inline
+// styles when it is explicitly allowed.
+function checkStylesAllowed(evt) {
+ var cspframe = document.getElementById('cspframe2');
+ var color;
+
+ // black means the style wasn't applied. green colors are used for styles
+ // expected to be applied. A color is red if a style is erroneously applied
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('linkstylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'External Stylesheet (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('inlinestylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'Inline Style TAG (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('attrstylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'Style Attribute (' + color + ')');
+
+ // Note that the below test will fail if "script-src: 'unsafe-inline'" breaks,
+ // since it relies on executing script to set .cssText
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('csstextstylediv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'style.cssText (' + color + ')');
+ // SMIL tests
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('xmlTest',null))['fill'];
+ ok('rgb(0, 255, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTest',null))['fill'];
+ ok('rgb(0, 255, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
+ ok('rgb(0, 255, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssSetTestById',null))['fill'];
+ ok('rgb(0, 255, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
+
+ color = window.getComputedStyle(cspframe.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
+ ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
+
+ checkIfDone();
+}
+
+function checkIfDone() {
+ done++;
+ if (done == 2)
+ SimpleTest.finish();
+}
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe1').src = 'file_inlinestyle_main.html';
+document.getElementById('cspframe1').addEventListener('load', checkStyles, false);
+document.getElementById('cspframe2').src = 'file_inlinestyle_main_allowed.html';
+document.getElementById('cspframe2').addEventListener('load', checkStylesAllowed, false);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_invalid_source_expression.html b/dom/security/test/csp/test_invalid_source_expression.html
new file mode 100644
index 000000000..ad0b34999
--- /dev/null
+++ b/dom/security/test/csp/test_invalid_source_expression.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1086612 - CSP: Let source expression be the empty set in case no valid source can be parsed</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We try to parse a policy:
+ * script-src bankid:/*
+ * where the source expression (bankid:/*) is invalid. In that case the source-expression
+ * should be the empty set ('none'), see: http://www.w3.org/TR/CSP11/#source-list-parsing
+ * We confirm that the script is blocked by CSP.
+ */
+
+const policy = "script-src bankid:/*";
+
+function runTest() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_invalid_source_expression.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(policy);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, "blocked", "should be 'blocked'!");
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content!");
+ }
+ SimpleTest.finish();
+}
+
+runTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_leading_wildcard.html b/dom/security/test/csp/test_leading_wildcard.html
new file mode 100644
index 000000000..81c9f58ec
--- /dev/null
+++ b/dom/security/test/csp/test_leading_wildcard.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1032303 - CSP - Keep FULL STOP when matching *.foo.com to disallow loads from foo.com</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page with a CSP that allows scripts to be loaded from *.example.com.
+ * On that page we try to load two scripts:
+ * a) [allowed] leading_wildcard_allowed.js which is served from test1.example.com
+ * b) [blocked] leading_wildcard_blocked.js which is served from example.com
+ *
+ * We verify that only the allowed script executes by registering observers which listen
+ * to CSP violations and http-notifications. Please note that both scripts do *not* exist
+ * in the file system. The test just verifies that CSP blocks correctly.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+var policy = "default-src 'none' script-src *.example.com";
+var testsExecuted = 0;
+
+function finishTest() {
+ if (++testsExecuted < 2) {
+ return;
+ }
+ window.wildCardExaminer.remove();
+ SimpleTest.finish();
+}
+
+// We use the examiner to identify requests that hit the wire and requests
+// that are blocked by CSP.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+
+ // allowed requests
+ if (topic === "specialpowers-http-notify-request") {
+ if (data.includes("leading_wildcard_allowed.js")) {
+ ok (true, "CSP should allow file_leading_wildcard_allowed.js!");
+ finishTest();
+ }
+ if (data.includes("leading_wildcard_blocked.js")) {
+ ok(false, "CSP should not allow file_leading_wildcard_blocked.js!");
+ finishTest();
+ }
+ }
+
+ // blocked requests
+ if (topic === "csp-on-violate-policy") {
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+
+ if (asciiSpec.includes("leading_wildcard_allowed.js")) {
+ ok (false, "CSP should not block file_leading_wildcard_allowed.js!");
+ finishTest();
+ }
+ if (asciiSpec.includes("leading_wildcard_blocked.js")) {
+ ok (true, "CSP should block file_leading_wildcard_blocked.js!");
+ finishTest();
+ }
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.wildCardExaminer = new examiner();
+
+function runTest() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_leading_wildcard.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(policy);
+
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+runTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_meta_element.html b/dom/security/test/csp/test_meta_element.html
new file mode 100644
index 000000000..98c12fce8
--- /dev/null
+++ b/dom/security/test/csp/test_meta_element.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 663570 - Implement Content Security Policy via <meta> tag</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe style="width:100%;" id="testframe" src="file_meta_element.html"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * The test is twofold:
+ * First, by loading a page using meta csp (into an iframe) we make sure that
+ * images get correctly blocked as the csp policy includes "img-src 'none'";
+ *
+ * Second, we make sure meta csp ignores the following directives:
+ * * report-uri
+ * * frame-ancestors
+ * * sandbox
+ *
+ * Please note that the CSP sanbdox directive (bug 671389) has not landed yet.
+ * Once bug 671389 lands this test will fail and needs to be updated.
+ */
+
+SimpleTest.waitForExplicitFinish();
+const EXPECTED_DIRS = ["img-src", "script-src"];
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ is(result, "img-blocked", "loading images should be blocked by meta csp");
+
+ try {
+ // get the csp in JSON notation from the principal
+ var frame = document.getElementById("testframe");
+ var principal = SpecialPowers.wrap(frame.contentDocument).nodePrincipal;
+ var cspJSON = principal.cspJSON;
+ ok(cspJSON, "CSP applied through meta element");
+
+ // parse the cspJSON in a csp-object
+ var cspOBJ = JSON.parse(cspJSON);
+ ok(cspOBJ, "was able to parse the JSON");
+
+ // make sure we only got one policy
+ var policies = cspOBJ["csp-policies"];
+ is(policies.length, 1, "there should be one policy applied");
+
+ // iterate the policy and make sure to only encounter
+ // expected directives.
+ var policy = policies[0];
+ for (var dir in policy) {
+ // special case handling for report-only which is not a directive
+ // but present in the JSON notation of the CSP.
+ if (dir === "report-only") {
+ continue;
+ }
+ var index = EXPECTED_DIRS.indexOf(dir);
+ isnot(index, -1, "meta csp contains directive: " + dir + "!");
+
+ // take the element out of the array so we can make sure
+ // that we have seen all the expected values in the end.
+ EXPECTED_DIRS.splice(index, 1);
+ }
+ is(EXPECTED_DIRS.length, 0, "have seen all the expected values");
+ }
+ catch (e) {
+ ok(false, "uuh, something went wrong within meta csp test");
+ }
+
+ finishTest();
+}
+
+// a postMessage handler used to bubble up the onsuccess/onerror state
+// from within the iframe.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_meta_header_dual.html b/dom/security/test/csp/test_meta_header_dual.html
new file mode 100644
index 000000000..4d6258d6e
--- /dev/null
+++ b/dom/security/test/csp/test_meta_header_dual.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 663570 - Implement Content Security Policy via meta tag</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We test all sorts of CSPs on documents, including documents with no
+ * CSP, with meta CSP and with meta CSP in combination with a CSP header.
+ */
+
+const TESTS = [
+ {
+ /* load image without any CSP */
+ query: "test1",
+ result: "img-loaded",
+ policyLen: 0,
+ desc: "no CSP should allow load",
+ },
+ {
+ /* load image where meta denies load */
+ query: "test2",
+ result: "img-blocked",
+ policyLen: 1,
+ desc: "meta (img-src 'none') should block load"
+ },
+ {
+ /* load image where meta allows load */
+ query: "test3",
+ result: "img-loaded",
+ policyLen: 1,
+ desc: "meta (img-src http://mochi.test) should allow load"
+ },
+ {
+ /* load image where meta allows but header blocks */
+ query: "test4", // triggers speculative load
+ result: "img-blocked",
+ policyLen: 2,
+ desc: "meta (img-src http://mochi.test), header (img-src 'none') should block load"
+ },
+ {
+ /* load image where meta blocks but header allows */
+ query: "test5", // triggers speculative load
+ result: "img-blocked",
+ policyLen: 2,
+ desc: "meta (img-src 'none'), header (img-src http://mochi.test) should block load"
+ },
+ {
+ /* load image where meta allows and header allows */
+ query: "test6", // triggers speculative load
+ result: "img-loaded",
+ policyLen: 2,
+ desc: "meta (img-src http://mochi.test), header (img-src http://mochi.test) should allow load"
+ },
+ {
+ /* load image where meta1 allows but meta2 blocks */
+ query: "test7",
+ result: "img-blocked",
+ policyLen: 2,
+ desc: "meta1 (img-src http://mochi.test), meta2 (img-src 'none') should allow blocked"
+ },
+ {
+ /* load image where meta1 allows and meta2 allows */
+ query: "test8",
+ result: "img-loaded",
+ policyLen: 2,
+ desc: "meta1 (img-src http://mochi.test), meta2 (img-src http://mochi.test) should allow allowed"
+ },
+];
+
+var curTest;
+var counter = -1;
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ // make sure the image got loaded or blocked
+ is(result, curTest.result, curTest.query + ": " + curTest.desc);
+
+ if (curTest.policyLen != 0) {
+ // make sure that meta policy got not parsed and appended twice
+ try {
+ // get the csp in JSON notation from the principal
+ var frame = document.getElementById("testframe");
+ var principal = SpecialPowers.wrap(frame.contentDocument).nodePrincipal;
+ var cspJSON = principal.cspJSON;
+ var cspOBJ = JSON.parse(cspJSON);
+
+ // make sure that the speculative policy and the actual policy
+ // are not appended twice.
+ var policies = cspOBJ["csp-policies"];
+ is(policies.length, curTest.policyLen, curTest.query + " should have: " + curTest.policyLen + " policies");
+ }
+ catch (e) {
+ ok(false, "uuh, something went wrong within cspToJSON in " + curTest.query);
+ }
+ }
+ // move on to the next test
+ runNextTest();
+}
+
+// a postMessage handler used to bubble up the
+// onsuccess/onerror state from within the iframe.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+function runNextTest() {
+ if (++counter == TESTS.length) {
+ finishTest();
+ return;
+ }
+ curTest = TESTS[counter];
+ // load next test
+ document.getElementById("testframe").src = "file_meta_header_dual.sjs?" + curTest.query;
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+runNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_meta_whitespace_skipping.html b/dom/security/test/csp/test_meta_whitespace_skipping.html
new file mode 100644
index 000000000..67c636c3b
--- /dev/null
+++ b/dom/security/test/csp/test_meta_whitespace_skipping.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1261634 - Update whitespace skipping for meta csp</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe" src="file_meta_whitespace_skipping.html"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load a site using meta CSP into an iframe. We make sure that all directives
+ * are parsed correclty by the CSP parser even though the directives are separated
+ * not only by whitespace but also by line breaks
+ */
+
+SimpleTest.waitForExplicitFinish();
+const EXPECTED_DIRS = [
+ "img-src", "script-src", "style-src", "child-src", "font-src"];
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ // sanity check that the site was loaded and the meta csp was parsed.
+ is(result, "meta-csp-parsed", "loading images should be blocked by meta csp");
+
+ try {
+ // get the csp in JSON notation from the principal
+ var frame = document.getElementById("testframe");
+ var principal = SpecialPowers.wrap(frame.contentDocument).nodePrincipal;
+ var cspJSON = principal.cspJSON;
+ ok(cspJSON, "CSP applied through meta element");
+
+ // parse the cspJSON in a csp-object
+ var cspOBJ = JSON.parse(cspJSON);
+ ok(cspOBJ, "was able to parse the JSON");
+
+ // make sure we only got one policy
+ var policies = cspOBJ["csp-policies"];
+ is(policies.length, 1, "there should be one policy applied");
+
+ // iterate the policy and make sure to only encounter
+ // expected directives.
+ var policy = policies[0];
+ for (var dir in policy) {
+ // special case handling for report-only which is not a directive
+ // but present in the JSON notation of the CSP.
+ if (dir === "report-only") {
+ continue;
+ }
+ var index = EXPECTED_DIRS.indexOf(dir);
+ isnot(index, -1, "meta csp contains directive: " + dir + "!");
+
+ // take the element out of the array so we can make sure
+ // that we have seen all the expected values in the end.
+ EXPECTED_DIRS.splice(index, 1);
+ }
+ is(EXPECTED_DIRS.length, 0, "have seen all the expected values");
+ }
+ catch (e) {
+ ok(false, "uuh, something went wrong within meta csp test");
+ }
+
+ finishTest();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_multi_policy_injection_bypass.html b/dom/security/test/csp/test_multi_policy_injection_bypass.html
new file mode 100644
index 000000000..83a9fa265
--- /dev/null
+++ b/dom/security/test/csp/test_multi_policy_injection_bypass.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717511
+-->
+<head>
+ <title>Test for Bug 717511</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<iframe style="width:200px;height:200px;" id='cspframe2'></iframe>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/dom/security/test/csp/";
+
+// These are test results: -1 means it hasn't run,
+// true/false is the pass/fail result.
+// This is not exhaustive, just double-checking the 'self' vs * policy conflict in the two HTTP headers.
+window.tests = {
+ img_good: -1,
+ img_bad: -1,
+ script_good: -1,
+ script_bad: -1,
+ img2_good: -1,
+ img2_bad: -1,
+ script2_good: -1,
+ script2_bad: -1,
+};
+
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be stopped!
+
+ if (topic === "specialpowers-http-notify-request") {
+ //these things were allowed by CSP
+ var asciiSpec = data;
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /_good/.test(testid),
+ asciiSpec + " allowed by csp");
+
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ // subject should be an nsIURI for csp-on-violate-policy
+ if (!SpecialPowers.can_QI(subject)) {
+ return;
+ }
+
+ //these were blocked... record that they were blocked
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"),
+ "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ window.testResult(testid,
+ /_bad/.test(testid),
+ asciiSpec + " blocked by \"" + data + "\"");
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+window.testResult = function(testname, result, msg) {
+
+ //test already complete.... forget it... remember the first result.
+ if (window.tests[testname] != -1)
+ return;
+
+ window.tests[testname] = result;
+ is(result, true, testname + ' test: ' + msg);
+
+ // if any test is incomplete, keep waiting
+ for (var v in window.tests)
+ if(tests[v] == -1)
+ return;
+
+ // ... otherwise, finish
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_multi_policy_injection_bypass.html';
+document.getElementById('cspframe2').src = 'file_multi_policy_injection_bypass_2.html';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_multipartchannel.html b/dom/security/test/csp/test_multipartchannel.html
new file mode 100644
index 000000000..3dd42783b
--- /dev/null
+++ b/dom/security/test/csp/test_multipartchannel.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1223743 - CSP: Check baseChannel for CSP when loading multipart channel</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We apply a CSP to a multipart channel and then try to load an image
+ * within a segment making sure the image is blocked correctly by CSP.
+ */
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ is(event.data, "img-blocked", "image should be blocked");
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+// start the test
+document.getElementById("testframe").src = "file_multipart_testserver.sjs?doc";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_nonce_source.html b/dom/security/test/csp/test_nonce_source.html
new file mode 100644
index 000000000..73a613576
--- /dev/null
+++ b/dom/security/test/csp/test_nonce_source.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test CSP 1.1 nonce-source for scripts and styles</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="visibility:hidden">
+ <iframe style="width:100%;" id='cspframe'></iframe>
+</div>
+<script class="testbody" type="text/javascript">
+
+var testsRun = 0;
+var totalTests = 20;
+
+// This is used to watch the blocked data bounce off CSP
+function examiner() {
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testid_re = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be blocked!
+
+ if (topic === "specialpowers-http-notify-request") {
+ var uri = data;
+ if (!testid_re.test(uri)) return;
+ var testid = testid_re.exec(uri)[1];
+ ok(/_good/.test(testid), "should allow URI with good testid " + testid);
+ ranTests(1);
+ }
+
+ if (topic === "csp-on-violate-policy") {
+ try {
+ // if it is an blocked external load, subject will be the URI of the resource
+ var blocked_uri = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testid_re.test(blocked_uri)) return;
+ var testid = testid_re.exec(blocked_uri)[1];
+ ok(/_bad/.test(testid), "should block URI with bad testid " + testid);
+ ranTests(1);
+ } catch (e) {
+ // if the subject is blocked inline, data will be a violation message
+ // we can't distinguish which resources triggered these, so we ignore them
+ }
+ }
+ },
+ // must eventually call this to remove the listener, or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+
+function cleanup() {
+ // remove the observer so we don't bork other tests
+ window.examiner.remove();
+ // finish the tests
+ SimpleTest.finish();
+}
+
+function ranTests(num) {
+ testsRun += num;
+ if (testsRun < totalTests) {
+ return;
+ }
+ cleanup();
+}
+
+function checkInlineScriptsAndStyles () {
+ var cspframe = document.getElementById('cspframe');
+ var getElementColorById = function (id) {
+ return window.getComputedStyle(cspframe.contentDocument.getElementById(id), null).color;
+ };
+ // Inline style tries to change an element's color to green. If blocked, the
+ // element's color will be the (unchanged) default black.
+ var green = "rgb(0, 128, 0)";
+ var red = "rgb(255,0,0)";
+ var black = "rgb(0, 0, 0)";
+
+ // inline script tests
+ is(getElementColorById('inline-script-correct-nonce'), green,
+ "Inline script with correct nonce should execute");
+ is(getElementColorById('inline-script-incorrect-nonce'), black,
+ "Inline script with incorrect nonce should not execute");
+ is(getElementColorById('inline-script-correct-style-nonce'), black,
+ "Inline script with correct nonce for styles (but not for scripts) should not execute");
+ is(getElementColorById('inline-script-no-nonce'), black,
+ "Inline script with no nonce should not execute");
+
+ // inline style tests
+ is(getElementColorById('inline-style-correct-nonce'), green,
+ "Inline style with correct nonce should be allowed");
+ is(getElementColorById('inline-style-incorrect-nonce'), black,
+ "Inline style with incorrect nonce should be blocked");
+ is(getElementColorById('inline-style-correct-script-nonce'), black,
+ "Inline style with correct nonce for scripts (but incorrect nonce for styles) should be blocked");
+ is(getElementColorById('inline-style-no-nonce'), black,
+ "Inline style with no nonce should be blocked");
+
+ ranTests(8);
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+window.examiner = new examiner();
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+document.getElementById('cspframe').src = 'file_nonce_source.html';
+document.getElementById('cspframe').addEventListener('load', checkInlineScriptsAndStyles, false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_null_baseuri.html b/dom/security/test/csp/test_null_baseuri.html
new file mode 100644
index 000000000..4592d582f
--- /dev/null
+++ b/dom/security/test/csp/test_null_baseuri.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121857 - document.baseURI should not get blocked if baseURI is null</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Creating a 'base' element and appending that element
+ * to document.head. After setting baseTag.href and finally
+ * removing the created element from the head, the baseURI
+ * should be the inital baseURI of the page.
+ */
+
+const TOTAL_TESTS = 3;
+var test_counter = 0;
+
+// a postMessage handler to communicate the results back to the parent.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event)
+{
+ // make sure the base-uri before and after the test is the initial base uri of the page
+ if (event.data.test === "initial_base_uri") {
+ ok(event.data.baseURI.startsWith("http://mochi.test"), "baseURI should be 'http://mochi.test'!");
+ }
+ // check that appending the child and setting the base tag actually affects the base-uri
+ else if (event.data.test === "changed_base_uri") {
+ ok(event.data.baseURI === "http://www.base-tag.com/", "baseURI should be 'http://www.base-tag.com'!");
+ }
+ // we shouldn't get here, but just in case, throw an error.
+ else {
+ ok(false, "unrecognized test!");
+ }
+
+ if (++test_counter === TOTAL_TESTS) {
+ SimpleTest.finish();
+ }
+}
+
+function startTest() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_null_baseuri.html");
+ // using 'unsafe-inline' since we load the testcase using an inline script
+ // within file_null_baseuri.html
+ src += "&csp=" + escape("default-src * 'unsafe-inline';");
+
+ document.getElementById("testframe").src = src;
+}
+
+
+SimpleTest.waitForExplicitFinish();
+startTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_path_matching.html b/dom/security/test/csp/test_path_matching.html
new file mode 100644
index 000000000..e02751803
--- /dev/null
+++ b/dom/security/test/csp/test_path_matching.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 808292 - Implement path-level host-source matching to CSP</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We are loading the following url (including a fragment portion):
+ * http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js#foo
+ * using different policies and verify that the applied policy is accurately enforced.
+ */
+
+var policies = [
+ ["allowed", "*"],
+ ["allowed", "http://*"], // test for bug 1075230, enforcing scheme and wildcard
+ ["allowed", "test1.example.com"],
+ ["allowed", "test1.example.com/"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/file_path_matching.js"],
+
+ ["allowed", "test1.example.com?foo=val"],
+ ["allowed", "test1.example.com/?foo=val"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/?foo=val"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/file_path_matching.js?foo=val"],
+
+ ["allowed", "test1.example.com#foo"],
+ ["allowed", "test1.example.com/#foo"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/#foo"],
+ ["allowed", "test1.example.com/tests/dom/security/test/csp/file_path_matching.js#foo"],
+
+ ["allowed", "*.example.com"],
+ ["allowed", "*.example.com/"],
+ ["allowed", "*.example.com/tests/dom/security/test/csp/"],
+ ["allowed", "*.example.com/tests/dom/security/test/csp/file_path_matching.js"],
+
+ ["allowed", "test1.example.com:80"],
+ ["allowed", "test1.example.com:80/"],
+ ["allowed", "test1.example.com:80/tests/dom/security/test/csp/"],
+ ["allowed", "test1.example.com:80/tests/dom/security/test/csp/file_path_matching.js"],
+
+ ["allowed", "test1.example.com:*"],
+ ["allowed", "test1.example.com:*/"],
+ ["allowed", "test1.example.com:*/tests/dom/security/test/csp/"],
+ ["allowed", "test1.example.com:*/tests/dom/security/test/csp/file_path_matching.js"],
+
+ ["blocked", "test1.example.com/tests"],
+ ["blocked", "test1.example.com/tests/dom/security/test/csp"],
+ ["blocked", "test1.example.com/tests/dom/security/test/csp/file_path_matching.py"],
+
+ ["blocked", "test1.example.com:8888/tests"],
+ ["blocked", "test1.example.com:8888/tests/dom/security/test/csp"],
+ ["blocked", "test1.example.com:8888/tests/dom/security/test/csp/file_path_matching.py"],
+
+ // case insensitive matching for scheme and host, but case sensitive matching for paths
+ ["allowed", "HTTP://test1.EXAMPLE.com/tests/"],
+ ["allowed", "test1.EXAMPLE.com/tests/"],
+ ["blocked", "test1.example.com/tests/dom/security/test/CSP/?foo=val"],
+ ["blocked", "test1.example.com/tests/dom/security/test/csp/FILE_path_matching.js?foo=val"],
+]
+
+var counter = 0;
+var policy;
+
+function loadNextTest() {
+ if (counter == policies.length) {
+ SimpleTest.finish();
+ }
+ else {
+ policy = policies[counter++];
+ var src = "file_testserver.sjs?file=";
+ // append the file that should be served
+ src += (counter % 2 == 0)
+ // load url including ref: example.com#foo
+ ? escape("tests/dom/security/test/csp/file_path_matching.html")
+ // load url including query: example.com?val=foo (bug 1147026)
+ : escape("tests/dom/security/test/csp/file_path_matching_incl_query.html");
+
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape("default-src 'none'; script-src " + policy[1]);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+ }
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, policy[0], "should be " + policy[0] + " in test " + (counter - 1) + "!");
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content in test " + (counter - 1) + "!");
+ }
+ loadNextTest();
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_path_matching_redirect.html b/dom/security/test/csp/test_path_matching_redirect.html
new file mode 100644
index 000000000..7d1044052
--- /dev/null
+++ b/dom/security/test/csp/test_path_matching_redirect.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 808292 - Implement path-level host-source matching to CSP (redirects)</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ <iframe style="width:100%;" id="testframe"></iframe>
+ </div>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * First, we try to load a script where the *path* does not match.
+ * Second, we try to load a script which is allowed by the CSPs
+ * script-src directive. The script then gets redirected to
+ * an URL where the host matches, but the path wouldn't.
+ * Since 'paths' should not be taken into account after redirects,
+ * that load should succeed. We are using a similar test setup
+ * as described in the spec, see:
+ * http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects
+ */
+
+var policy = "script-src http://example.com http://test1.example.com/CSPAllowsScriptsInThatFolder";
+
+var tests = [
+ {
+ // the script in file_path_matching.html
+ // <script src="http://test1.example.com/tests/dom/security/..">
+ // is not within the whitelisted path by the csp-policy
+ // hence the script is 'blocked' by CSP.
+ expected: "blocked",
+ uri: "tests/dom/security/test/csp/file_path_matching.html"
+ },
+ {
+ // the script in file_path_matching_redirect.html
+ // <script src="http://example.com/tests/dom/..">
+ // gets redirected to: http://test1.example.com/tests/dom
+ // where after the redirect the path of the policy is not enforced
+ // anymore and hence execution of the script is 'allowed'.
+ expected: "allowed",
+ uri: "tests/dom/security/test/csp/file_path_matching_redirect.html"
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function checkResult() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', checkResult, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, curTest.expected, "should be blocked in test " + (counter - 1) + "!");
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content in test " + (counter - 1) + "!");
+ }
+ loadNextTest();
+}
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ }
+ else {
+ curTest = tests[counter++];
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape(curTest.uri);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(policy);
+
+ document.getElementById("testframe").addEventListener("load", checkResult, false);
+ document.getElementById("testframe").src = src;
+ }
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_ping.html b/dom/security/test/csp/test_ping.html
new file mode 100644
index 000000000..a896794e1
--- /dev/null
+++ b/dom/security/test/csp/test_ping.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1100181 - CSP: Enforce connect-src when submitting pings</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load a page with a given CSP and verify that hyperlink auditing
+ * is correctly evaluated through the "connect-src" directive.
+ */
+
+// Need to pref hyperlink auditing on since it's disabled by default.
+SpecialPowers.setBoolPref("browser.send_pings", true);
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ {
+ result : "allowed",
+ policy : "connect-src 'self'"
+ },
+ {
+ result : "blocked",
+ policy : "connect-src 'none'"
+ }
+];
+
+// initializing to -1 so we start at index 0 when we start the test
+var counter = -1;
+
+function checkResult(aResult) {
+ is(aResult, tests[counter].result, "should be " + tests[counter].result + " in test " + counter + "!");
+ loadNextTest();
+}
+
+// We use the examiner to identify requests that hit the wire and requests
+// that are blocked by CSP and bubble up the result to the including iframe
+// document (parent).
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "specialpowers-http-notify-request") {
+ // making sure we do not bubble a result for something
+ // other then the request in question.
+ if (!data.includes("send-ping")) {
+ return;
+ }
+ checkResult("allowed");
+ return;
+ }
+
+ if (topic === "csp-on-violate-policy") {
+ // making sure we do not bubble a result for something
+ // other then the request in question.
+ var asciiSpec = SpecialPowers.getPrivilegedProps(
+ SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!asciiSpec.includes("send-ping")) {
+ return;
+ }
+ checkResult("blocked");
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.ConnectSrcExaminer = new examiner();
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ window.ConnectSrcExaminer.remove();
+ SimpleTest.finish();
+ return;
+ }
+
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_ping.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(tests[counter].policy);
+
+ document.getElementById("testframe").src = src;
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_policyuri_regression_from_multipolicy.html b/dom/security/test/csp/test_policyuri_regression_from_multipolicy.html
new file mode 100644
index 000000000..f69e8f558
--- /dev/null
+++ b/dom/security/test/csp/test_policyuri_regression_from_multipolicy.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 924708</title>
+ <!--
+ test that a report-only policy that uses policy-uri is not incorrectly
+ enforced due to regressions introduced by Bug 836922.
+ -->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:200px;height:200px;" id='testframe'></iframe>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var testframe = document.getElementById('testframe');
+testframe.src = 'file_policyuri_regression_from_multipolicy.html';
+testframe.addEventListener('load', function checkInlineScriptExecuted () {
+ is(this.contentDocument.getElementById('testdiv').innerHTML,
+ 'Inline Script Executed',
+ 'Inline script should execute (it would be blocked by the policy, but the policy is report-only)');
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_redirects.html b/dom/security/test/csp/test_redirects.html
new file mode 100644
index 000000000..df01e3b41
--- /dev/null
+++ b/dom/security/test/csp/test_redirects.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for Content Security Policy during redirects</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<iframe style="width:100%;height:300px;" id="harness"></iframe>
+<pre id="log"></pre>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/dom/security/test/csp/";
+
+// debugging
+function log(s) {
+ return;
+ dump("**" + s + "\n");
+ var log = document.getElementById("log");
+ log.textContent = log.textContent+s+"\n";
+}
+
+// used to watch if requests are blocked by CSP or allowed through
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9-]+)");
+ var asciiSpec;
+ var testid;
+
+ if (topic === "specialpowers-http-notify-request") {
+ // request was sent
+ var allowedUri = data;
+ if (!testpat.test(allowedUri)) return;
+ testid = testpat.exec(allowedUri)[1];
+ if (testExpectedResults[testid] == "completed") return;
+ log("allowed: "+allowedUri);
+ window.testResult(testid, allowedUri, true);
+ }
+
+ else if (topic === "csp-on-violate-policy") {
+ // request was blocked
+ asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ testid = testpat.exec(asciiSpec)[1];
+ // had to add this check because http-on-modify-request can fire after
+ // csp-on-violate-policy, apparently, even though the request does
+ // not hit the wire.
+ if (testExpectedResults[testid] == "completed") return;
+ log("BLOCKED: "+asciiSpec);
+ window.testResult(testid, asciiSpec, false);
+ }
+ },
+
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+window.examiner = new examiner();
+
+// contains { test_frame_id : expected_result }
+var testExpectedResults = { "font-src": true,
+ "font-src-redir": false,
+ "frame-src": true,
+ "frame-src-redir": false,
+ "img-src": true,
+ "img-src-redir": false,
+ "media-src": true,
+ "media-src-redir": false,
+ "object-src": true,
+ "object-src-redir": false,
+ "script-src": true,
+ "script-src-redir": false,
+ "style-src": true,
+ "style-src-redir": false,
+ "xhr-src": true,
+ "xhr-src-redir": false,
+ "from-worker": true,
+ "script-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+ "xhr-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+ "fetch-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
+ "from-blob-worker": true,
+ "script-src-redir-from-blob-worker": false,
+ "xhr-src-redir-from-blob-worker": false,
+ "fetch-src-redir-from-blob-worker": false,
+ "img-src-from-css": true,
+ "img-src-redir-from-css": false,
+ };
+
+// takes the name of the test, the URL that was tested, and whether the
+// load occurred
+var testResult = function(testName, url, result) {
+ log(" testName: "+testName+", result: "+result+", expected: "+testExpectedResults[testName]+"\n");
+ is(result, testExpectedResults[testName], testName+" test: "+url);
+
+ // mark test as completed
+ testExpectedResults[testName] = "completed";
+
+ // don't finish until we've run all the tests
+ for (var t in testExpectedResults) {
+ if (testExpectedResults[t] != "completed") {
+ return;
+ }
+ }
+
+ window.examiner.remove();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+ {'set':[// This defaults to 0 ("preload none") on mobile (B2G/Android), which
+ // blocks loading the resource until the user interacts with a
+ // corresponding widget, which breaks the media_* tests. We set it
+ // back to the default used by desktop Firefox to get consistent
+ // behavior.
+ ["media.preload.default", 2]]},
+ function() {
+ // save this for last so that our listeners are registered.
+ // ... this loads the testbed of good and bad requests.
+ document.getElementById("harness").src = "file_redirects_main.html";
+ });
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/security/test/csp/test_referrerdirective.html b/dom/security/test/csp/test_referrerdirective.html
new file mode 100644
index 000000000..770fcc40b
--- /dev/null
+++ b/dom/security/test/csp/test_referrerdirective.html
@@ -0,0 +1,145 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=965727
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Content Security Policy referrer Directive (Bug 965727)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+/*
+ * This tests various referrer policies and the referrer-sending behavior when
+ * requesting scripts in different ways:
+ * - cross-origin (https://example.com -> https://test2.example.com)
+ * - same-origin (https://example.com -> https://example.com)
+ * - downgrade (https://example.com -> http://example.com)
+ *
+ * Each test creates an iframe that loads scripts for each of the checks. If
+ * the scripts are blocked, the test fails (they should run). When loaded,
+ * each script updates a results object in the test page, and then when the
+ * test page has finished loading all the scripts, it postMessages back to this
+ * page. Once all tests are done, the results are checked.
+ */
+
+var testData = {
+ 'default': { 'csp': "script-src * 'unsafe-inline'; referrer default",
+ 'expected': { 'sameorigin': 'full',
+ 'crossorigin': 'full',
+ 'downgrade': 'none' }},
+
+ 'origin': { 'csp': "script-src * 'unsafe-inline'; referrer origin",
+ 'expected': { 'sameorigin': 'origin',
+ 'crossorigin': 'origin',
+ 'downgrade': 'origin' }},
+
+ 'origin-when-cross-origin': { 'csp': "script-src * 'unsafe-inline'; referrer origin-when-cross-origin",
+ 'expected': { 'sameorigin': 'full',
+ 'crossorigin': 'origin',
+ 'downgrade': 'origin' }},
+
+ 'unsafe-url': { 'csp': "script-src * 'unsafe-inline'; referrer unsafe-url",
+ 'expected': { 'sameorigin': 'full',
+ 'crossorigin': 'full',
+ 'downgrade': 'full' }},
+
+ 'none': { 'csp': "script-src * 'unsafe-inline'; referrer no-referrer",
+ 'expected': { 'sameorigin': 'none',
+ 'crossorigin': 'none',
+ 'downgrade': 'none' }},
+
+ // referrer delivered through CSPRO should be ignored
+ 'ignore-cspro': { 'cspro': "script-src * 'unsafe-inline'; referrer origin",
+ 'expected': { 'sameorigin': 'full',
+ 'crossorigin': 'full',
+ 'downgrade': 'none' }},
+
+ // referrer delivered through CSPRO should be ignored
+ 'ignore-cspro2': { 'csp' : "script-src * 'unsafe-inline'; referrer no-referrer",
+ 'cspro': "script-src * 'unsafe-inline'; referrer origin",
+ 'expected': { 'sameorigin': 'none',
+ 'crossorigin': 'none',
+ 'downgrade': 'none' }},
+ };
+
+var referrerDirectiveTests = {
+ // called via postMessage when one of the iframes is done running.
+ onIframeComplete: function(event) {
+ try {
+ var results = JSON.parse(event.data);
+ ok(results.hasOwnProperty('id'), "'id' property required in posted message " + event.data);
+
+ ok(testData.hasOwnProperty(results['id']), "Test " + results['id'] + " must be expected.");
+
+ // check all the various load types' referrers.
+ var expected = testData[results['id']].expected;
+ for (var t in expected) {
+ is(results.results[t], expected[t],
+ " referrer must match expected for " + t + " in " + results['id']);
+ }
+ testData[results['id']]['complete'] = true;
+
+ } catch(e) {
+ // fail -- should always be JSON
+ ok(false, "failed to parse posted message + " + event.data);
+ // have to end as well since not all messages were valid.
+ SimpleTest.finish();
+ }
+
+ referrerDirectiveTests.checkForCompletion();
+ },
+
+ // checks to see if all the parallel tests are done and validates results.
+ checkForCompletion: function() {
+ for (var id in testData) {
+ if (!testData[id].hasOwnProperty('complete')) {
+ return;
+ }
+ }
+ SimpleTest.finish();
+ }
+};
+
+SimpleTest.waitForExplicitFinish();
+// have to disable mixed content blocking to test https->http referrers.
+SpecialPowers.pushPrefEnv({
+ 'set': [['security.mixed_content.block_active_content', false],
+ ['security.mixed_content.block_display_content', false],
+ ['security.mixed_content.send_hsts_priming', false],
+ ['security.mixed_content.use_hsts', false],
+ ]
+ },
+ function() {
+ // each of the iframes we create will call us back when its contents are loaded.
+ window.addEventListener("message", referrerDirectiveTests.onIframeComplete.bind(window), false);
+
+ // one iframe created for each test case
+ for (var id in testData) {
+ var elt = document.createElement("iframe");
+ var src = "https://example.com/tests/dom/security/test/csp/file_testserver.sjs?id=" + id;
+ if (testData[id]['csp']) {
+ src += "&csp=" + escape(testData[id]['csp']);
+ }
+ if (testData[id]['cspro']) {
+ src += "&cspro=" + escape(testData[id]['cspro']);
+ }
+ src += "&file=tests/dom/security/test/csp/file_referrerdirective.html";
+ elt.src = src;
+ document.getElementById("content").appendChild(elt);
+ }
+ });
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_report.html b/dom/security/test/csp/test_report.html
new file mode 100644
index 000000000..e8cfb8778
--- /dev/null
+++ b/dom/security/test/csp/test_report.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=548193
+-->
+<head>
+ <title>Test for Bug 548193</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We try to load an inline-src using a policy that constrains
+ * all scripts from running (default-src 'none'). We verify that
+ * the generated csp-report contains the expceted values. If any
+ * of the JSON is not formatted properly (e.g. not properly escaped)
+ * then JSON.parse will fail, which allows to pinpoint such errors
+ * in the catch block, and the test will fail. Since we use an
+ * observer, we can set the actual report-uri to a foo value.
+ */
+
+const testfile = "tests/dom/security/test/csp/file_report.html";
+const reportURI = "http://mochi.test:8888/foo.sjs";
+const policy = "default-src 'none'; report-uri " + reportURI;
+const docUri = "http://mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs" +
+ "?file=tests/dom/security/test/csp/file_report.html" +
+ "&csp=default-src%20%27none%27%3B%20report-uri%20http%3A//mochi.test%3A8888/foo.sjs";
+
+window.checkResults = function(reportObj) {
+ var cspReport = reportObj["csp-report"];
+
+ // The following uris' fragments should be stripped before reporting:
+ // * document-uri
+ // * blocked-uri
+ // * source-file
+ // see http://www.w3.org/TR/CSP11/#violation-reports
+ is(cspReport["document-uri"], docUri, "Incorrect document-uri");
+
+ // we can not test for the whole referrer since it includes platform specific information
+ ok(cspReport["referrer"].startsWith("http://mochi.test:8888/tests/dom/security/test/csp/test_report.html"),
+ "Incorrect referrer");
+
+ is(cspReport["blocked-uri"], "self", "Incorrect blocked-uri");
+
+ is(cspReport["violated-directive"], "default-src 'none'", "Incorrect violated-directive");
+
+ is(cspReport["original-policy"], "default-src 'none'; report-uri http://mochi.test:8888/foo.sjs",
+ "Incorrect original-policy");
+
+ is(cspReport["source-file"], docUri, "Incorrect source-file");
+
+ is(cspReport["script-sample"], "\n var foo = \"propEscFoo\";\n var bar...",
+ "Incorrect script-sample");
+
+ is(cspReport["line-number"], 7, "Incorrect line-number");
+}
+
+var chromeScriptUrl = SimpleTest.getTestFileURL("file_report_chromescript.js");
+var script = SpecialPowers.loadChromeScript(chromeScriptUrl);
+
+script.addMessageListener('opening-request-completed', function ml(msg) {
+ if (msg.error) {
+ ok(false, "Could not query report (exception: " + msg.error + ")");
+ } else {
+ try {
+ var reportObj = JSON.parse(msg.report);
+ } catch (e) {
+ ok(false, "Could not parse JSON (exception: " + e + ")");
+ }
+ try {
+ // test for the proper values in the report object
+ window.checkResults(reportObj);
+ } catch (e) {
+ ok(false, "Could not query report (exception: " + e + ")");
+ }
+ }
+
+ script.removeMessageListener('opening-request-completed', ml);
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+// load the resource which will generate a CSP violation report
+// save this for last so that our listeners are registered.
+var src = "file_testserver.sjs";
+// append the file that should be served
+src += "?file=" + escape(testfile);
+// append the CSP that should be used to serve the file
+src += "&csp=" + escape(policy);
+// appending a fragment so we can test that it's correctly stripped
+// for document-uri and source-file.
+src += "#foo";
+document.getElementById("cspframe").src = src;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_report_for_import.html b/dom/security/test/csp/test_report_for_import.html
new file mode 100644
index 000000000..be112d51c
--- /dev/null
+++ b/dom/security/test/csp/test_report_for_import.html
@@ -0,0 +1,112 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=548193
+-->
+<head>
+ <title>Test for Bug 548193</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We are loading a stylesheet using a csp policy that only allows styles from 'self'
+ * to be loaded. In other words, the *.css file itself should be allowed to load, but
+ * the @import file within the CSS should get blocked. We verify that the generated
+ * csp-report is sent and contains all the expected values.
+ * In detail, the test starts by sending an XHR request to the report-server
+ * which waits on the server side till the report was received and hands the
+ * report in JSON format back to the testfile which then verifies accuracy
+ * of all the different report fields in the CSP report.
+ */
+
+const TEST_FILE = "tests/dom/security/test/csp/file_report_for_import.html";
+const REPORT_URI =
+ "http://mochi.test:8888/tests/dom/security/test/csp/file_report_for_import_server.sjs?report";
+const POLICY = "style-src 'self'; report-uri " + REPORT_URI;
+
+const DOC_URI =
+ "http://mochi.test:8888/tests/dom/security/test/csp/file_testserver.sjs?" +
+ "file=tests/dom/security/test/csp/file_report_for_import.html&" +
+ "csp=style-src%20%27self%27%3B%20" +
+ "report-uri%20http%3A//mochi.test%3A8888/tests/dom/security/test/csp/" +
+ "file_report_for_import_server.sjs%3Freport";
+
+function checkResults(reportStr) {
+ try {
+ var reportObj = JSON.parse(reportStr);
+ var cspReport = reportObj["csp-report"];
+
+ is(cspReport["document-uri"], DOC_URI, "Incorrect document-uri");
+ is(cspReport["referrer"],
+ "http://mochi.test:8888/tests/dom/security/test/csp/test_report_for_import.html",
+ "Incorrect referrer");
+ is(cspReport["violated-directive"],
+ "style-src http://mochi.test:8888",
+ "Incorrect violated-directive");
+ is(cspReport["original-policy"],
+ "style-src http://mochi.test:8888; report-uri " +
+ "http://mochi.test:8888/tests/dom/security/test/csp/file_report_for_import_server.sjs?report",
+ "Incorrect original-policy");
+ is(cspReport["blocked-uri"],
+ "http://example.com",
+ "Incorrect blocked-uri");
+
+ // we do not always set the following fields
+ is(cspReport["source-file"], undefined, "Incorrect source-file");
+ is(cspReport["script-sample"], undefined, "Incorrect script-sample");
+ is(cspReport["line-number"], undefined, "Incorrect line-number");
+ }
+ catch (e) {
+ ok(false, "Could not parse JSON (exception: " + e + ")");
+ }
+}
+
+function loadTestPageIntoFrame() {
+ // load the resource which will generate a CSP violation report
+ // save this for last so that our listeners are registered.
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape(TEST_FILE);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(POLICY);
+ // appending a fragment so we can test that it's correctly stripped
+ // for document-uri and source-file.
+ src += "#foo";
+ document.getElementById("cspframe").src = src;
+}
+
+function runTest() {
+ // send an xhr request to the server which is processed async, which only
+ // returns after the server has received the csp report.
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "file_report_for_import_server.sjs?queryresult");
+ myXHR.onload = function(e) {
+ checkResults(myXHR.responseText);
+ SimpleTest.finish();
+ }
+ myXHR.onerror = function(e) {
+ ok(false, "could not query results from server (" + e.message + ")");
+ SimpleTest.finish();
+ }
+ myXHR.send();
+
+ // give it some time and run the testpage
+ SimpleTest.executeSoon(loadTestPageIntoFrame);
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html b/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html
new file mode 100644
index 000000000..d02911141
--- /dev/null
+++ b/dom/security/test/csp/test_report_uri_missing_in_report_only_header.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=847081
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 847081</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=847081">Mozilla Bug 847081</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<iframe id="cspframe"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"]
+ .getService(SpecialPowers.Ci.nsIStringBundleService);
+var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties");
+var warningMsg = localizer.formatStringFromName("reportURInotInReportOnlyHeader", [window.location.origin], 1);
+function cleanup() {
+ SpecialPowers.postConsoleSentinel();
+ SimpleTest.finish();
+}
+
+SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
+ if (aMsg.message.indexOf(warningMsg) > -1) {
+ ok(true, "report-uri not specified in Report-Only should throw a CSP warning.");
+ SimpleTest.executeSoon(cleanup);
+ return;
+ } else {
+ // if some other console message is present, we wait
+ return;
+ }
+});
+
+
+// set up and start testing
+SimpleTest.waitForExplicitFinish();
+document.getElementById('cspframe').src = 'file_report_uri_missing_in_report_only_header.html';
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_require_sri_meta.html b/dom/security/test/csp/test_require_sri_meta.html
new file mode 100644
index 000000000..a06fe122a
--- /dev/null
+++ b/dom/security/test/csp/test_require_sri_meta.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1277557 - CSP require-sri-for does not block when CSP is in meta tag</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load scripts within an iframe and make sure that meta-csp of
+ * require-sri-for applies correctly to preloaded scripts.
+ * Please note that we have to use <script src=""> to kick
+ * off the html preloader.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.setBoolPref("security.csp.experimentalEnabled", true);
+
+var curTest;
+var counter = -1;
+
+const tests = [
+ { // test 1
+ description: "script with *no* SRI should be blocked",
+ query: "no-sri",
+ expected: "script-blocked"
+ },
+ { // test 2
+ description: "script-with *incorrect* SRI should be blocked",
+ query: "wrong-sri",
+ expected: "script-blocked"
+ },
+ { // test 3
+ description: "script-with *correct* SRI should be loaded",
+ query: "correct-sri",
+ expected: "script-loaded"
+ },
+];
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ is(result, curTest.expected, curTest.description);
+ loadNextTest();
+}
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+function loadNextTest() {
+ counter++;
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ curTest = tests[counter];
+ var testframe = document.getElementById("testframe");
+ testframe.src = "file_require_sri_meta.sjs?" + curTest.query;
+}
+
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_sandbox.html b/dom/security/test/csp/test_sandbox.html
new file mode 100644
index 000000000..f0df66d6a
--- /dev/null
+++ b/dom/security/test/csp/test_sandbox.html
@@ -0,0 +1,249 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for bugs 886164 and 671389</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+
+<script class="testbody" type="text/javascript">
+
+var testCases = [
+ {
+ // Test 1: don't load image from non-same-origin; allow loading
+ // images from same-same origin
+ sandboxAttribute: "allow-same-origin",
+ csp: "default-src 'self'",
+ file: "file_sandbox_1.html",
+ results: { img1a_good: -1, img1_bad: -1 }
+ // fails if scripts execute
+ },
+ {
+ // Test 2: don't load image from non-same-origin; allow loading
+ // images from same-same origin, even without allow-same-origin
+ // flag
+ sandboxAttribute: "",
+ csp: "default-src 'self'",
+ file: "file_sandbox_2.html",
+ results: { img2_bad: -1, img2a_good: -1 }
+ // fails if scripts execute
+ },
+ {
+ // Test 3: disallow loading images from any host, even with
+ // allow-same-origin flag set
+ sandboxAttribute: "allow-same-origin",
+ csp: "default-src 'none'",
+ file: "file_sandbox_3.html",
+ results: { img3_bad: -1, img3a_bad: -1 },
+ // fails if scripts execute
+ },
+ {
+ // Test 4: disallow loading images from any host
+ sandboxAttribute: "",
+ csp: "default-src 'none'",
+ file: "file_sandbox_4.html",
+ results: { img4_bad: -1, img4a_bad: -1 }
+ // fails if scripts execute
+ },
+ {
+ // Test 5: disallow loading images or scripts, allow inline scripts
+ sandboxAttribute: "allow-scripts",
+ csp: "default-src 'none'; script-src 'unsafe-inline';",
+ file: "file_sandbox_5.html",
+ results: { img5_bad: -1, img5a_bad: -1, script5_bad: -1, script5a_bad: -1 },
+ nrOKmessages: 2 // sends 2 ok message
+ // fails if scripts execute
+ },
+ {
+ // Test 6: disallow non-same-origin images, allow inline and same origin scripts
+ sandboxAttribute: "allow-same-origin allow-scripts",
+ csp: "default-src 'self' 'unsafe-inline';",
+ file: "file_sandbox_6.html",
+ results: { img6_bad: -1, script6_bad: -1 },
+ nrOKmessages: 4 // sends 4 ok message
+ // fails if forms are not disallowed
+ },
+ {
+ // Test 7: same as Test 1
+ csp: "default-src 'self'; sandbox allow-same-origin",
+ file: "file_sandbox_7.html",
+ results: { img7a_good: -1, img7_bad: -1 }
+ },
+ {
+ // Test 8: same as Test 2
+ csp: "sandbox allow-same-origin; default-src 'self'",
+ file: "file_sandbox_8.html",
+ results: { img8_bad: -1, img8a_good: -1 }
+ },
+ {
+ // Test 9: same as Test 3
+ csp: "default-src 'none'; sandbox allow-same-origin",
+ file: "file_sandbox_9.html",
+ results: { img9_bad: -1, img9a_bad: -1 }
+ },
+ {
+ // Test 10: same as Test 4
+ csp: "default-src 'none'; sandbox allow-same-origin",
+ file: "file_sandbox_10.html",
+ results: { img10_bad: -1, img10a_bad: -1 }
+ },
+ {
+ // Test 11: same as Test 5
+ csp: "default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts allow-same-origin",
+ file: "file_sandbox_11.html",
+ results: { img11_bad: -1, img11a_bad: -1, script11_bad: -1, script11a_bad: -1 },
+ nrOKmessages: 2 // sends 2 ok message
+ },
+ {
+ // Test 12: same as Test 6
+ csp: "sandbox allow-same-origin allow-scripts; default-src 'self' 'unsafe-inline';",
+ file: "file_sandbox_12.html",
+ results: { img12_bad: -1, script12_bad: -1 },
+ nrOKmessages: 4 // sends 4 ok message
+ },
+ {
+ // Test 13: same as Test 5 and Test 11, but:
+ // * using sandbox flag 'allow-scripts' in CSP and not as iframe attribute
+ // * not using allow-same-origin in CSP (so a new NullPrincipal is created).
+ csp: "default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts",
+ file: "file_sandbox_13.html",
+ results: { img13_bad: -1, img13a_bad: -1, script13_bad: -1, script13a_bad: -1 },
+ nrOKmessages: 2 // sends 2 ok message
+ },
+];
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like:
+// { ok: true/false,
+// desc: <description of the test> which it then forwards to ok() }
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+var totalTests = (function() {
+ var nrCSPloadTests = 0;
+ for(var i = 0; i < testCases.length; i++) {
+ nrCSPloadTests += Object.keys(testCases[i].results).length;
+ if (testCases[i].nrOKmessages) {
+ // + number of expected postMessages from iframe
+ nrCSPloadTests += testCases[i].nrOKmessages;
+ }
+ }
+ return nrCSPloadTests;
+})();
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests === totalTests) {
+ window.examiner.remove();
+ SimpleTest.finish();
+ }
+}
+
+// Set the iframe src and sandbox attribute
+function runTest(test) {
+ var iframe = document.createElement('iframe');
+
+ document.getElementById('content').appendChild(iframe);
+
+ // set sandbox attribute
+ if (test.sandboxAttribute !== undefined) {
+ iframe.sandbox = test.sandboxAttribute;
+ }
+
+ // set query string
+ var src = 'file_testserver.sjs';
+ // path where the files are
+ var path = '/tests/dom/security/test/csp/';
+
+ src += '?file=' + escape(path+test.file);
+
+ if (test.csp !== undefined) {
+ src += '&csp=' + escape(test.csp);
+ }
+
+ iframe.src = src;
+ iframe.width = iframe.height = 10;
+}
+
+// Examiner related
+
+// This is used to watch the blocked data bounce off CSP and allowed data
+// get sent out to the wire.
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+ SpecialPowers.addObserver(this, "specialpowers-http-notify-request", false);
+}
+
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ var testpat = new RegExp("testid=([a-z0-9_]+)");
+
+ //_good things better be allowed!
+ //_bad things better be stopped!
+
+ if (topic === "specialpowers-http-notify-request") {
+ //these things were allowed by CSP
+ var uri = data;
+ if (!testpat.test(uri)) return;
+ var testid = testpat.exec(uri)[1];
+
+ if(/_good/.test(testid)) {
+ ok_wrapper(true, uri + " is allowed by csp");
+ } else {
+ ok_wrapper(false, uri + " should not be allowed by csp");
+ }
+ }
+
+ if(topic === "csp-on-violate-policy") {
+ //these were blocked... record that they were blocked
+ var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ if (!testpat.test(asciiSpec)) return;
+ var testid = testpat.exec(asciiSpec)[1];
+ if(/_bad/.test(testid)) {
+ ok_wrapper(true, asciiSpec + " was blocked by \"" + data + "\"");
+ } else {
+ ok_wrapper(false, asciiSpec + " should have been blocked by \"" + data + "\"");
+ }
+ }
+ },
+
+ // must eventually call this to remove the listener,
+ // or mochitests might get borked.
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
+ }
+}
+
+window.examiner = new examiner();
+
+SimpleTest.waitForExplicitFinish();
+
+(function() { // Run tests:
+ for(var i = 0; i < testCases.length; i++) {
+ runTest(testCases[i]);
+ }
+})();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_sandbox_allow_scripts.html b/dom/security/test/csp/test_sandbox_allow_scripts.html
new file mode 100644
index 000000000..10acaae43
--- /dev/null
+++ b/dom/security/test/csp/test_sandbox_allow_scripts.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1396320: Fix CSP sandbox regression for allow-scripts</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Load an iframe using a CSP of 'sandbox allow-scripts' and make sure
+ * the security context of the iframe is sandboxed (cross origin)
+ */
+SimpleTest.waitForExplicitFinish();
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+ is(event.data.result, "",
+ "document.domain of sandboxed iframe should be opaque");
+ window.removeEventListener("message", receiveMessage);
+ SimpleTest.finish();
+}
+
+let testframe = document.getElementById("testframe");
+testframe.src = "file_sandbox_allow_scripts.html";
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_scheme_relative_sources.html b/dom/security/test/csp/test_scheme_relative_sources.html
new file mode 100644
index 000000000..21271cdd1
--- /dev/null
+++ b/dom/security/test/csp/test_scheme_relative_sources.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 921493 - CSP: test whitelisting of scheme-relative sources</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We load http and https pages and verify that scheme relative sources
+ * are allowed unless its a downgrade from https -> http.
+ *
+ * Please note that the policy contains 'unsafe-inline' so we can use
+ * an inline script to query the result from within the sandboxed iframe
+ * and report it back to the parent document.
+ */
+
+var POLICY = "default-src 'none'; script-src 'unsafe-inline' example.com;";
+
+var tests = [
+ {
+ description: "http -> http",
+ from: "http",
+ to: "http",
+ result: "allowed",
+ },
+ {
+ description: "http -> https",
+ from: "http",
+ to: "https",
+ result: "allowed",
+ },
+ {
+ description: "https -> https",
+ from: "https",
+ to: "https",
+ result: "allowed",
+ },
+ {
+ description: "https -> http",
+ from: "https",
+ to: "http",
+ result: "blocked",
+ }
+];
+
+var counter = 0;
+var curTest;
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+ return;
+ }
+
+ curTest = tests[counter++];
+
+ var src = curTest.from +
+ "://example.com/tests/dom/security/test/csp/file_scheme_relative_sources.sjs" +
+ "?scheme=" + curTest.to +
+ "&policy=" + escape(POLICY);
+
+ document.getElementById("testframe").src = src;
+}
+
+// using a postMessage handler to report the result back from
+// within the sandboxed iframe without 'allow-same-origin'.
+window.addEventListener("message", receiveMessage, false);
+
+function receiveMessage(event) {
+
+ is(event.data.result, curTest.result,
+ "should be " + curTest.result + " in test (" + curTest.description + ")!");
+
+ loadNextTest();
+}
+
+// get the test started
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_self_none_as_hostname_confusion.html b/dom/security/test/csp/test_self_none_as_hostname_confusion.html
new file mode 100644
index 000000000..e5b93c538
--- /dev/null
+++ b/dom/security/test/csp/test_self_none_as_hostname_confusion.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=587377
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 587377</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=587377">Mozilla Bug 587377</a>
+<p id="display"></p>
+
+<iframe id="cspframe"></iframe>
+
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+// Load locale string during mochitest
+var stringBundleService = SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"]
+ .getService(SpecialPowers.Ci.nsIStringBundleService);
+var localizer = stringBundleService.createBundle("chrome://global/locale/security/csp.properties");
+var confusionMsg = localizer.formatStringFromName("hostNameMightBeKeyword", ["SELF", "self"], 2);
+
+function cleanup() {
+ SpecialPowers.postConsoleSentinel();
+ SimpleTest.finish();
+};
+
+// To prevent the test from asserting twice and calling SimpleTest.finish() twice,
+// startTest will be marked false as soon as the confusionMsg is detected.
+startTest = false;
+SpecialPowers.registerConsoleListener(function ConsoleMsgListener(aMsg) {
+ if (startTest) {
+ if (aMsg.message.indexOf(confusionMsg) > -1) {
+ startTest = false;
+ ok(true, "CSP header with a hostname similar to keyword should be warned");
+ SimpleTest.executeSoon(cleanup);
+ } else {
+ // don't see the warning yet? wait.
+ return;
+ }
+ }
+});
+
+// set up and start testing
+SimpleTest.waitForExplicitFinish();
+document.getElementById('cspframe').src = 'file_self_none_as_hostname_confusion.html';
+startTest = true;
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_sendbeacon.html b/dom/security/test/csp/test_sendbeacon.html
new file mode 100644
index 000000000..1b4cfbc86
--- /dev/null
+++ b/dom/security/test/csp/test_sendbeacon.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1234813 - sendBeacon should not throw if blocked by Content Policy</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<iframe style="width:100%;" id="testframe" src="file_sendbeacon.html"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Let's try to fire a sendBeacon which gets blocked by CSP. Let's make sure
+ * sendBeacon does not throw an exception.
+ */
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler used to bubble up the
+// result from within the iframe.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ var result = event.data.result;
+ is(result, "blocked-beacon-does-not-throw", "sendBeacon should not throw");
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_service_worker.html b/dom/security/test/csp/test_service_worker.html
new file mode 100644
index 000000000..0cff84751
--- /dev/null
+++ b/dom/security/test/csp/test_service_worker.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1208559 - ServiceWorker registration not governed by CSP</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Spawning a worker from https://example.com but script-src is 'test1.example.com'
+ * CSP is not consulted
+ */
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ {
+ policy: "default-src 'self'; script-src 'unsafe-inline'; child-src test1.example.com;",
+ expected: "blocked"
+ },
+];
+
+var counter = 0;
+var curTest;
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ is(event.data.result, curTest.expected, "Should be (" + curTest.expected + ") in Test " + counter + "!");
+ loadNextTest();
+}
+
+onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true]
+ ]}, loadNextTest);
+}
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+ curTest = tests[counter++];
+ var src = "https://example.com/tests/dom/security/test/csp/file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape("tests/dom/security/test/csp/file_service_worker.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+ document.getElementById("testframe").src = src;
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_shouldprocess.html b/dom/security/test/csp/test_shouldprocess.html
new file mode 100644
index 000000000..5d0925167
--- /dev/null
+++ b/dom/security/test/csp/test_shouldprocess.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=908933
+-->
+<head>
+ <title>Test Bug 908933</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+/*
+ * Description of the test:
+ * We load variations of 'objects' and make sure all the
+ * resource loads are correctly blocked by CSP.
+ * For all the testing we use a CSP with "object-src 'none'"
+ * so that all the loads are either blocked by
+ * shouldProcess or shouldLoad.
+ */
+
+const POLICY = "default-src http://mochi.test:8888; object-src 'none'";
+const TESTFILE = "tests/dom/security/test/csp/file_shouldprocess.html";
+
+SimpleTest.waitForExplicitFinish();
+
+var tests = [
+ // Note that the files listed below don't actually exist.
+ // Since loading of them should be blocked by shouldProcess, we don't
+ // really need these files.
+
+ // blocked by shouldProcess
+ "http://mochi.test:8888/tests/dom/security/test/csp/test1",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test2",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test3",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test4",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test5",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test6",
+ // blocked by shouldLoad
+ "http://mochi.test:8888/tests/dom/security/test/csp/test7.class",
+ "http://mochi.test:8888/tests/dom/security/test/csp/test8.class",
+];
+
+function checkResults(aURI) {
+ var index = tests.indexOf(aURI);
+ if (index > -1) {
+ tests.splice(index, 1);
+ ok(true, "ShouldLoad or ShouldProcess blocks TYPE_OBJECT with uri: " + aURI + "!");
+ }
+ else {
+ ok(false, "ShouldLoad or ShouldProcess incorreclty blocks TYPE_OBJECT with uri: " + aURI + "!");
+ }
+ if (tests.length == 0) {
+ window.examiner.remove();
+ SimpleTest.finish();
+ }
+}
+
+// used to watch that shouldProcess blocks TYPE_OBJECT
+function examiner() {
+ SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
+}
+examiner.prototype = {
+ observe: function(subject, topic, data) {
+ if (topic === "csp-on-violate-policy") {
+ var asciiSpec =
+ SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
+ checkResults(asciiSpec);
+ }
+ },
+ remove: function() {
+ SpecialPowers.removeObserver(this, "csp-on-violate-policy");
+ }
+}
+window.examiner = new examiner();
+
+function loadFrame() {
+ var src = "file_testserver.sjs";
+ // append the file that should be served
+ src += "?file=" + escape(TESTFILE);
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(POLICY);
+
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ document.body.appendChild(iframe);
+}
+
+SpecialPowers.pushPrefEnv(
+ { "set": [['plugin.java.mime', 'application/x-java-test']] },
+ loadFrame);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_strict_dynamic.html b/dom/security/test/csp/test_strict_dynamic.html
new file mode 100644
index 000000000..00e75143f
--- /dev/null
+++ b/dom/security/test/csp/test_strict_dynamic.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.setBoolPref("security.csp.enableStrictDynamic", true);
+
+/* Description of the test:
+ * We load scripts with a CSP of 'strict-dynamic' with valid
+ * and invalid nonces and make sure scripts are allowed/blocked
+ * accordingly. Different tests load inline and external scripts
+ * also using a CSP including http: and https: making sure
+ * other srcs are invalided by 'strict-dynamic'.
+ */
+
+var tests = [
+ {
+ desc: "strict-dynamic with valid nonce should be allowed",
+ result: "allowed",
+ file: "file_strict_dynamic_script_extern.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' https: 'none' 'self'"
+ },
+ {
+ desc: "strict-dynamic with invalid nonce should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_extern.html",
+ policy: "script-src 'strict-dynamic' 'nonce-bar' http: http://example.com"
+ },
+ {
+ desc: "strict-dynamic, whitelist and invalid nonce should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_extern.html",
+ policy: "script-src 'strict-dynamic' 'nonce-bar' 'unsafe-inline' http: http://example.com"
+ },
+ {
+ desc: "strict-dynamic with no 'nonce-' should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_extern.html",
+ policy: "script-src 'strict-dynamic'"
+ },
+ // inline scripts
+ {
+ desc: "strict-dynamic with valid nonce should be allowed",
+ result: "allowed",
+ file: "file_strict_dynamic_script_inline.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' https: 'none' 'self'"
+ },
+ {
+ desc: "strict-dynamic with invalid nonce should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_inline.html",
+ policy: "script-src 'strict-dynamic' 'nonce-bar' http: http://example.com"
+ },
+ {
+ desc: "strict-dynamic, unsafe-inline and invalid nonce should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_inline.html",
+ policy: "script-src 'strict-dynamic' 'nonce-bar' 'unsafe-inline' http: http://example.com"
+ },
+ {
+ desc: "strict-dynamic with no 'nonce-' should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_inline.html",
+ policy: "script-src 'strict-dynamic'"
+ },
+ {
+ desc: "strict-dynamic with DOM events should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_script_events.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo'"
+ },
+ {
+ // marquee is a special snowflake. Extra test for xbl things.
+ desc: "strict-dynamic with DOM events should be blocked (XBL)",
+ result: "blocked",
+ file: "file_strict_dynamic_script_events_xbl.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo'"
+ },
+ {
+ desc: "strict-dynamic with JS URLs should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_js_url.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo'"
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ curTest = tests[counter++];
+ var src = "file_testserver.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/" + curTest.file)
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, curTest.result, curTest.desc);
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content for test: '" + curTest.desc + "'");
+ }
+ loadNextTest();
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_strict_dynamic_default_src.html b/dom/security/test/csp/test_strict_dynamic_default_src.html
new file mode 100644
index 000000000..17518444e
--- /dev/null
+++ b/dom/security/test/csp/test_strict_dynamic_default_src.html
@@ -0,0 +1,136 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.setBoolPref("security.csp.enableStrictDynamic", true);
+
+/* Description of the test:
+ * We load scripts and images with a CSP of 'strict-dynamic' making sure
+ * whitelists get ignored for scripts but not for images when strict-dynamic
+ * appears in default-src.
+ *
+ * Please note that we do not support strict-dynamic within default-src yet,
+ * see Bug 1313937. When updating this test please do not change the
+ * csp policies, but only replace todo_is() with is().
+ */
+
+var tests = [
+ {
+ script_desc: "(test1) script should be allowed because of valid nonce",
+ img_desc: "(test1) img should be allowed because of 'self'",
+ script_result: "allowed",
+ img_result: "allowed",
+ policy: "default-src 'strict-dynamic' 'self'; script-src 'nonce-foo'"
+ },
+ {
+ script_desc: "(test 2) script should be blocked because of invalid nonce",
+ img_desc: "(test 2) img should be allowed because of valid scheme-src",
+ script_result: "blocked",
+ img_result: "allowed",
+ policy: "default-src 'strict-dynamic' http:; script-src 'nonce-bar' http:"
+ },
+ {
+ script_desc: "(test 3) script should be blocked because of invalid nonce",
+ img_desc: "(test 3) img should be allowed because of valid host-src",
+ script_result: "blocked",
+ script_enforced: "",
+ img_result: "allowed",
+ policy: "default-src 'strict-dynamic' mochi.test; script-src 'nonce-bar' http:"
+ },
+ {
+ script_desc: "(test 4) script should be allowed because of valid nonce",
+ img_desc: "(test 4) img should be blocked because of default-src 'strict-dynamic'",
+ script_result: "allowed",
+ img_result: "blocked",
+ policy: "default-src 'strict-dynamic'; script-src 'nonce-foo'"
+ },
+ // some reverse order tests (have script-src appear before default-src)
+ {
+ script_desc: "(test 5) script should be allowed because of valid nonce",
+ img_desc: "(test 5) img should be blocked because of default-src 'strict-dynamic'",
+ script_result: "allowed",
+ img_result: "blocked",
+ policy: "script-src 'nonce-foo'; default-src 'strict-dynamic';"
+ },
+ {
+ script_desc: "(test 6) script should be allowed because of valid nonce",
+ img_desc: "(test 6) img should be blocked because of default-src http:",
+ script_result: "blocked",
+ img_result: "allowed",
+ policy: "script-src 'nonce-bar' http:; default-src 'strict-dynamic' http:;"
+ },
+ {
+ script_desc: "(test 7) script should be allowed because of invalid nonce",
+ img_desc: "(test 7) img should be blocked because of image-src http:",
+ script_result: "blocked",
+ img_result: "allowed",
+ policy: "script-src 'nonce-bar' http:; default-src 'strict-dynamic' http:; img-src http:"
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ curTest = tests[counter++];
+ var src = "file_testserver.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/file_strict_dynamic_default_src.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+
+ document.getElementById("testframe").addEventListener("load", checkResults, false);
+ document.getElementById("testframe").src = src;
+}
+
+function checkResults() {
+ try {
+ var testframe = document.getElementById("testframe");
+ testframe.removeEventListener('load', checkResults, false);
+
+ // check if script loaded
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ if (curTest.script_result === "blocked") {
+ todo_is(divcontent, curTest.script_result, curTest.script_desc);
+ }
+ else {
+ is(divcontent, curTest.script_result, curTest.script_desc);
+ }
+
+ // check if image loaded
+ var testimg = testframe.contentWindow.document.getElementById("testimage");
+ if (curTest.img_result === "allowed") {
+ ok(testimg.complete, curTest.img_desc);
+ }
+ else {
+ ok((testimg.width == 0) && (testimg.height == 0), curTest.img_desc);
+ }
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content for test: '" + curTest.script_desc + "'");
+ }
+
+ loadNextTest();
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_strict_dynamic_parser_inserted.html b/dom/security/test/csp/test_strict_dynamic_parser_inserted.html
new file mode 100644
index 000000000..9e588660d
--- /dev/null
+++ b/dom/security/test/csp/test_strict_dynamic_parser_inserted.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1299483 - CSP: Implement 'strict-dynamic'</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.setBoolPref("security.csp.enableStrictDynamic", true);
+
+/* Description of the test:
+ * We loader parser and non parser inserted scripts making sure that
+ * parser inserted scripts are blocked if strict-dynamic is present
+ * and no valid nonce and also making sure that non-parser inserted
+ * scripts are allowed to execute.
+ */
+
+var tests = [
+ {
+ desc: "(parser inserted script) using doc.write(<script>) should be blocked",
+ result: "blocked",
+ file: "file_strict_dynamic_parser_inserted_doc_write.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' http:"
+ },
+ {
+ desc: "(parser inserted script with valid nonce) using doc.write(<script>) should be allowed",
+ result: "allowed",
+ file: "file_strict_dynamic_parser_inserted_doc_write_correct_nonce.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' https:"
+ },
+ {
+ desc: "(non parser inserted script) using appendChild() should allow external script",
+ result: "allowed",
+ file: "file_strict_dynamic_non_parser_inserted.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' https:"
+ },
+ {
+ desc: "(non parser inserted script) using appendChild() should allow inline script",
+ result: "allowed",
+ file: "file_strict_dynamic_non_parser_inserted_inline.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' https:"
+ },
+ {
+ desc: "strict-dynamic should not invalidate 'unsafe-eval'",
+ result: "allowed",
+ file: "file_strict_dynamic_unsafe_eval.html",
+ policy: "script-src 'strict-dynamic' 'nonce-foo' 'unsafe-eval'"
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function loadNextTest() {
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ curTest = tests[counter++];
+ var src = "file_testserver.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/" + curTest.file)
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+
+ document.getElementById("testframe").addEventListener("load", test, false);
+ document.getElementById("testframe").src = src;
+}
+
+function test() {
+ try {
+ document.getElementById("testframe").removeEventListener('load', test, false);
+ var testframe = document.getElementById("testframe");
+ var divcontent = testframe.contentWindow.document.getElementById('testdiv').innerHTML;
+ is(divcontent, curTest.result, curTest.desc);
+ }
+ catch (e) {
+ ok(false, "ERROR: could not access content for test: '" + curTest.desc + "'");
+ }
+ loadNextTest();
+}
+
+// start running the tests
+loadNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_subframe_run_js_if_allowed.html b/dom/security/test/csp/test_subframe_run_js_if_allowed.html
new file mode 100644
index 000000000..ccc81a265
--- /dev/null
+++ b/dom/security/test/csp/test_subframe_run_js_if_allowed.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=702439
+
+This test verifies that child iframes of CSP documents are
+permitted to execute javascript: URLs assuming the policy
+allows this.
+-->
+<head>
+ <title>Test for Bug 702439</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe id="i"></iframe>
+<script class="testbody" type="text/javascript">
+var javascript_link_ran = false;
+
+// check that the script in the child frame's javascript: URL ran
+function checkResult()
+{
+ is(javascript_link_ran, true,
+ "javascript URL didn't execute");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+document.getElementById('i').src = 'file_subframe_run_js_if_allowed.html';
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure.html b/dom/security/test/csp/test_upgrade_insecure.html
new file mode 100644
index 000000000..a2b99b8db
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure.html
@@ -0,0 +1,181 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load resources (img, script, sytle, etc) over *http* and make sure
+ * that all the resources get upgraded to use >> https << when the
+ * csp-directive "upgrade-insecure-requests" is specified. We further
+ * test that subresources within nested contexts (iframes) get upgraded
+ * and also test the handling of server side redirects.
+ *
+ * In detail:
+ * We perform an XHR request to the *.sjs file which is processed async on
+ * the server and waits till all the requests were processed by the server.
+ * Once the server received all the different requests, the server responds
+ * to the initial XHR request with an array of results which must match
+ * the expected results from each test, making sure that all requests
+ * received by the server (*.sjs) were actually *https* requests.
+ */
+
+const UPGRADE_POLICY =
+ "upgrade-insecure-requests;" + // upgrade all http requests to https
+ "block-all-mixed-content;" + // upgrade should be enforced before block-all.
+ "default-src https: wss: 'unsafe-inline';" + // only allow https: and wss:
+ "form-action https:;"; // explicit, no fallback to default-src
+
+const UPGRADE_POLICY_NO_DEFAULT_SRC =
+ "upgrade-insecure-requests;" + // upgrade all http requests to https
+ "script-src 'unsafe-inline' *"; // we have to whitelist the inline scripts
+ // in the test.
+const NO_UPGRADE_POLICY =
+ "default-src http: ws: 'unsafe-inline';" + // allow http:// and ws://
+ "form-action http:;"; // explicit, no fallback to default-src
+
+var tests = [
+ { // (1) test that all requests within an >> https << page get updated
+ policy: UPGRADE_POLICY,
+ topLevelScheme: "https://",
+ description: "upgrade all requests on toplevel https",
+ deliveryMethod: "header",
+ results: [
+ "iframe-ok", "script-ok", "img-ok", "img-redir-ok", "font-ok", "xhr-ok", "style-ok",
+ "media-ok", "object-ok", "form-ok", "websocket-ok", "nested-img-ok"
+ ]
+ },
+ { // (2) test that all requests within an >> http << page get updated
+ policy: UPGRADE_POLICY,
+ topLevelScheme: "http://",
+ description: "upgrade all requests on toplevel http",
+ deliveryMethod: "header",
+ results: [
+ "iframe-ok", "script-ok", "img-ok", "img-redir-ok", "font-ok", "xhr-ok", "style-ok",
+ "media-ok", "object-ok", "form-ok", "websocket-ok", "nested-img-ok"
+ ]
+ },
+ { // (3) test that all requests within an >> http << page get updated, but do
+ // not specify a default-src directive.
+ policy: UPGRADE_POLICY_NO_DEFAULT_SRC,
+ topLevelScheme: "http://",
+ description: "upgrade all requests on toplevel http where default-src is not specified",
+ deliveryMethod: "header",
+ results: [
+ "iframe-ok", "script-ok", "img-ok", "img-redir-ok", "font-ok", "xhr-ok", "style-ok",
+ "media-ok", "object-ok", "form-ok", "websocket-ok", "nested-img-ok"
+ ]
+ },
+ { // (4) test that no requests get updated if >> upgrade-insecure-requests << is not used
+ policy: NO_UPGRADE_POLICY,
+ topLevelScheme: "http://",
+ description: "do not upgrade any requests on toplevel http",
+ deliveryMethod: "header",
+ results: [
+ "iframe-error", "script-error", "img-error", "img-redir-error", "font-error",
+ "xhr-error", "style-error", "media-error", "object-error", "form-error",
+ "websocket-error", "nested-img-error"
+ ]
+ },
+ { // (5) test that all requests within an >> https << page using meta CSP get updated
+ // policy: UPGRADE_POLICY, that test uses UPGRADE_POLICY within
+ // file_upgrade_insecure_meta.html
+ // no need to define it within that object.
+ topLevelScheme: "https://",
+ description: "upgrade all requests on toplevel https using meta csp",
+ deliveryMethod: "meta",
+ results: [
+ "iframe-ok", "script-ok", "img-ok", "img-redir-ok", "font-ok", "xhr-ok", "style-ok",
+ "media-ok", "object-ok", "form-ok", "websocket-ok", "nested-img-ok"
+ ]
+ },
+];
+
+var counter = 0;
+var curTest;
+
+function loadTestPage() {
+ curTest = tests[counter++];
+ var src = curTest.topLevelScheme + "example.com/tests/dom/security/test/csp/file_testserver.sjs?file=";
+ if (curTest.deliveryMethod === "header") {
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/file_upgrade_insecure.html");
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(curTest.policy);
+ }
+ else {
+ src += escape("tests/dom/security/test/csp/file_upgrade_insecure_meta.html");
+ // no csp here, since it's in the meta element
+ }
+ document.getElementById("testframe").src = src;
+}
+
+function finishTest() {
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function checkResults(result) {
+ // try to find the expected result within the results array
+ var index = curTest.results.indexOf(result);
+ isnot(index, -1, curTest.description + " (result: " + result + ")");
+
+ // take the element out the array and continue till the results array is empty
+ if (index != -1) {
+ curTest.results.splice(index, 1);
+ }
+ // lets check if we are expecting more results to bubble up
+ if (curTest.results.length > 0) {
+ return;
+ }
+ // lets see if we ran all the tests
+ if (counter == tests.length) {
+ finishTest();
+ return;
+ }
+ // otherwise it's time to run the next test
+ runNextTest();
+}
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to bubble up results back to this main page.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResults(event.data.result);
+}
+
+function runNextTest() {
+ // sends an xhr request to the server which is processed async, which only
+ // returns after the server has received all the expected requests.
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "file_upgrade_insecure_server.sjs?queryresult");
+ myXHR.onload = function(e) {
+ var results = myXHR.responseText.split(",");
+ for (var index in results) {
+ checkResults(results[index]);
+ }
+ }
+ myXHR.onerror = function(e) {
+ ok(false, "could not query results from server (" + e.message + ")");
+ finishTest();
+ }
+ myXHR.send();
+
+ // give it some time and run the testpage
+ SimpleTest.executeSoon(loadTestPage);
+}
+
+SimpleTest.waitForExplicitFinish();
+runNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure_cors.html b/dom/security/test/csp/test_upgrade_insecure_cors.html
new file mode 100644
index 000000000..af296983c
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure_cors.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load a page serving two XHR requests (including being redirected);
+ * one that should not require CORS and one that should require cors, in particular:
+ *
+ * Test 1:
+ * Main page: https://test1.example.com
+ * XHR request: http://test1.example.com
+ * Redirect to: http://test1.example.com
+ * Description: Upgrade insecure should upgrade from http to https and also
+ * surpress CORS for that case.
+ *
+ * Test 2:
+ * Main page: https://test1.example.com
+ * XHR request: http://test1.example.com
+ * Redirect to: http://test1.example.com:443
+ * Description: Upgrade insecure should upgrade from http to https and also
+ * prevent CORS for that case.
+ * Note: If redirecting to a different port, then CORS *should* be enforced (unless
+ * it's port 443). Unfortunately we can't test that because of the setup of our
+ * *.sjs files; they only are able to listen to port 443, see:
+ * http://mxr.mozilla.org/mozilla-central/source/build/pgo/server-locations.txt#98
+ *
+ * Test 3:
+ * Main page: https://test1.example.com
+ * XHR request: http://test2.example.com
+ * Redirect to: http://test1.example.com
+ * Description: Upgrade insecure should *not* prevent CORS since
+ * the page performs a cross origin xhr.
+ *
+ */
+
+const CSP_POLICY = "upgrade-insecure-requests; script-src 'unsafe-inline'";
+var tests = 3;
+
+function loadTest() {
+ var src = "https://test1.example.com/tests/dom/security/test/csp/file_testserver.sjs?file=";
+ // append the file that should be served
+ src += escape("tests/dom/security/test/csp/file_upgrade_insecure_cors.html")
+ // append the CSP that should be used to serve the file
+ src += "&csp=" + escape(CSP_POLICY);
+ document.getElementById("testframe").src = src;
+}
+
+function checkResult(result) {
+ if (result === "test1-no-cors-ok" ||
+ result === "test2-no-cors-diffport-ok" ||
+ result === "test3-cors-ok") {
+ ok(true, "'upgrade-insecure-requests' acknowledges CORS (" + result + ")");
+ }
+ else {
+ ok(false, "'upgrade-insecure-requests' acknowledges CORS (" + result + ")");
+ }
+ if (--tests > 0) {
+ return;
+ }
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+// a postMessage handler that is used to bubble up results from
+// within the iframe.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ checkResult(event.data);
+}
+
+SimpleTest.waitForExplicitFinish();
+loadTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure_docwrite_iframe.html b/dom/security/test/csp/test_upgrade_insecure_docwrite_iframe.html
new file mode 100644
index 000000000..822158bd7
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure_docwrite_iframe.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1273430 - Test CSP upgrade-insecure-requests for doc.write(iframe)</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Load an iframe which ships with a CSP of upgrade-insecure-requests.
+ * Within that iframe a script performs doc.write(iframe) using an
+ * *http* URL. Make sure, the URL is upgraded to *https*.
+ *
+ * +-----------------------------------------+
+ * | |
+ * | http(s); csp: upgrade-insecure-requests | |
+ * | +---------------------------------+ |
+ * | | | |
+ * | | doc.write(<iframe src='http'>); | <--------- upgrade to https
+ * | | | |
+ * | +---------------------------------+ |
+ * | |
+ * +-----------------------------------------+
+ *
+ */
+
+const TEST_FRAME_URL =
+ "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs?testframe";
+
+// important: the RESULT should have a scheme of *https*
+const RESULT =
+ "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_docwrite_iframe.sjs?docwriteframe";
+
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ is(event.data.result, RESULT, "doc.write(iframe) of http should be upgraded to https!");
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+var testframe = document.getElementById("testframe");
+testframe.src = TEST_FRAME_URL;
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure_referrer.html b/dom/security/test/csp/test_upgrade_insecure_referrer.html
new file mode 100644
index 000000000..890c57335
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure_referrer.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load a page that makes use of the CSP referrer directive as well
+ * as upgrade-insecure-requests. The page loads an image over http.
+ * The test makes sure the request gets upgraded to https and the
+ * correct referrer gets sent.
+ */
+
+var tests = [
+ {
+ query: "test1",
+ description: "upgrade insecure request with 'referrer = origin' (CSP in header)",
+ result: "http://example.com/"
+ },
+ {
+ query: "test2",
+ description: "upgrade insecure request with 'referrer = no-referrer' (CSP in header)",
+ result: ""
+ },
+ {
+ query: "test3",
+ description: "upgrade insecure request with 'referrer = origin' (Meta CSP)",
+ result: "http://example.com/"
+ },
+ {
+ query: "test4",
+ description: "upgrade insecure request with 'referrer = no-referrer' (Meta CSP)",
+ result: ""
+ }
+];
+
+var counter = 0;
+var curTest;
+
+function loadTestPage() {
+ curTest = tests[counter++];
+ var src = "http://example.com/tests/dom/security/test/csp/file_upgrade_insecure_referrer.sjs?";
+ // append the query
+ src += curTest.query;
+ document.getElementById("testframe").src = src;
+}
+
+function runNextTest() {
+ // sends a request to the server which is processed async and returns
+ // once the server received the expected image request
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "file_upgrade_insecure_referrer_server.sjs?queryresult");
+ myXHR.onload = function(e) {
+ is(myXHR.responseText, curTest.result, curTest.description);
+ if (counter == tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+ // move on to the next test by setting off another query request.
+ runNextTest();
+ }
+ myXHR.onerror = function(e) {
+ ok(false, "could not query results from server (" + e.message + ")");
+ SimpleTest.finish();
+ }
+ myXHR.send();
+
+ // give it some time and load the testpage
+ SimpleTest.executeSoon(loadTestPage);
+}
+
+SimpleTest.waitForExplicitFinish();
+runNextTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/csp/test_upgrade_insecure_reporting.html b/dom/security/test/csp/test_upgrade_insecure_reporting.html
new file mode 100644
index 000000000..967654179
--- /dev/null
+++ b/dom/security/test/csp/test_upgrade_insecure_reporting.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1139297 - Implement CSP upgrade-insecure-requests directive</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * We load an https page which includes an http image. We make sure that
+ * the image request gets upgraded to https but also make sure that a report
+ * is sent when a CSP report only is used which only allows https requests.
+ */
+
+var expectedResults = 2;
+
+function finishTest() {
+ // let's wait till the image was loaded and the report was received
+ if (--expectedResults > 0) {
+ return;
+ }
+ window.removeEventListener("message", receiveMessage, false);
+ SimpleTest.finish();
+}
+
+function runTest() {
+ // (1) Lets send off an XHR request which will return once the server receives
+ // the violation report from the report only policy.
+ var myXHR = new XMLHttpRequest();
+ myXHR.open("GET", "file_upgrade_insecure_reporting_server.sjs?queryresult");
+ myXHR.onload = function(e) {
+ is(myXHR.responseText, "report-ok", "csp-report was sent correctly");
+ finishTest();
+ }
+ myXHR.onerror = function(e) {
+ ok(false, "could not query result for csp-report from server (" + e.message + ")");
+ finishTest();
+ }
+ myXHR.send();
+
+ // (2) We load a page that is served using a CSP and a CSP report only which loads
+ // an image over http.
+ SimpleTest.executeSoon(function() {
+ document.getElementById("testframe").src =
+ "https://example.com/tests/dom/security/test/csp/file_upgrade_insecure_reporting_server.sjs?toplevel";
+ });
+}
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to bubble up results back to this main page.
+window.addEventListener("message", receiveMessage, false);
+function receiveMessage(event) {
+ // (3) make sure the image was correctly loaded
+ is(event.data.result, "img-ok", "upgraded insecure image load from http -> https");
+ finishTest();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/general/bug1277803.html b/dom/security/test/general/bug1277803.html
new file mode 100644
index 000000000..c8033551a
--- /dev/null
+++ b/dom/security/test/general/bug1277803.html
@@ -0,0 +1,11 @@
+<html>
+
+<head>
+ <link rel='icon' href='favicon_bug1277803.ico'>
+</head>
+
+<body>
+Nothing to see here...
+</body>
+
+</html>
diff --git a/dom/security/test/general/chrome.ini b/dom/security/test/general/chrome.ini
new file mode 100644
index 000000000..94bf1ef05
--- /dev/null
+++ b/dom/security/test/general/chrome.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ favicon_bug1277803.ico
+ bug1277803.html
+
+[test_bug1277803.xul]
+skip-if = os == 'android'
diff --git a/dom/security/test/general/favicon_bug1277803.ico b/dom/security/test/general/favicon_bug1277803.ico
new file mode 100644
index 000000000..d44438903
--- /dev/null
+++ b/dom/security/test/general/favicon_bug1277803.ico
Binary files differ
diff --git a/dom/security/test/general/file_block_script_wrong_mime_server.sjs b/dom/security/test/general/file_block_script_wrong_mime_server.sjs
new file mode 100644
index 000000000..d6d27796c
--- /dev/null
+++ b/dom/security/test/general/file_block_script_wrong_mime_server.sjs
@@ -0,0 +1,34 @@
+// Custom *.sjs specifically for the needs of:
+// Bug 1288361 - Block scripts with wrong MIME type
+
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const WORKER = `
+ onmessage = function(event) {
+ postMessage("worker-loaded");
+ };`;
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // Set MIME type
+ response.setHeader("Content-Type", query.get("mime"), false);
+
+ // Deliver response
+ switch (query.get("type")) {
+ case "script":
+ response.write("");
+ break;
+ case "worker":
+ response.write(WORKER);
+ break;
+ case "worker-import":
+ response.write(`importScripts("file_block_script_wrong_mime_server.sjs?type=script&mime=${query.get("mime")}");`);
+ response.write(WORKER);
+ break;
+ }
+}
diff --git a/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs b/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs
new file mode 100644
index 000000000..f0084410a
--- /dev/null
+++ b/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs
@@ -0,0 +1,46 @@
+// custom *.sjs for Bug 1255240
+
+const TEST_FRAME = `
+ <!DOCTYPE HTML>
+ <html>
+ <head><meta charset='utf-8'></head>
+ <body>
+ <a id='testlink' target='innerframe' href='file_contentpolicytype_targeted_link_iframe.sjs?innerframe'>click me</a>
+ <iframe name='innerframe'></iframe>
+ <script type='text/javascript'>
+ var link = document.getElementById('testlink');
+ testlink.click();
+ </script>
+ </body>
+ </html> `;
+
+const INNER_FRAME = `
+ <!DOCTYPE HTML>
+ <html>
+ <head><meta charset='utf-8'></head>
+ hello world!
+ </body>
+ </html>`;
+
+function handleRequest(request, response)
+{
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html", false);
+
+ var queryString = request.queryString;
+
+ if (queryString === "testframe") {
+ response.write(TEST_FRAME);
+ return;
+ }
+
+ if (queryString === "innerframe") {
+ response.write(INNER_FRAME);
+ return;
+ }
+
+ // we should never get here, but just in case
+ // return something unexpected
+ response.write("do'h");
+}
diff --git a/dom/security/test/general/file_nosniff_testserver.sjs b/dom/security/test/general/file_nosniff_testserver.sjs
new file mode 100644
index 000000000..0cf168a3c
--- /dev/null
+++ b/dom/security/test/general/file_nosniff_testserver.sjs
@@ -0,0 +1,60 @@
+"use strict";
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+const SCRIPT = "var foo = 24;";
+const CSS = "body { background-color: green; }";
+
+// small red image
+const IMG = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==");
+
+function handleRequest(request, response) {
+ const query = new URLSearchParams(request.queryString);
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // set the nosniff header
+ response.setHeader("X-Content-Type-Options", " NoSniFF , foo ", false);
+
+ if (query.has("cssCorrectType")) {
+ response.setHeader("Content-Type", "teXt/cSs", false);
+ response.write(CSS);
+ return;
+ }
+
+ if (query.has("cssWrongType")) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(CSS);
+ return;
+ }
+
+ if (query.has("scriptCorrectType")) {
+ response.setHeader("Content-Type", "appLIcation/jAvaScriPt;blah", false);
+ response.write(SCRIPT);
+ return;
+ }
+
+ if (query.has("scriptWrongType")) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(SCRIPT);
+ return;
+ }
+
+ if (query.has("imgCorrectType")) {
+ response.setHeader("Content-Type", "iMaGe/pnG;blah", false);
+ response.write(IMG);
+ return;
+ }
+
+ if (query.has("imgWrongType")) {
+ response.setHeader("Content-Type", "text/html", false);
+ response.write(IMG);
+ return;
+ }
+
+ // we should never get here, but just in case
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("do'h");
+}
diff --git a/dom/security/test/general/mochitest.ini b/dom/security/test/general/mochitest.ini
new file mode 100644
index 000000000..70c0c9fb6
--- /dev/null
+++ b/dom/security/test/general/mochitest.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ file_contentpolicytype_targeted_link_iframe.sjs
+ file_nosniff_testserver.sjs
+ file_block_script_wrong_mime_server.sjs
+
+[test_contentpolicytype_targeted_link_iframe.html]
+[test_nosniff.html]
+[test_block_script_wrong_mime.html]
diff --git a/dom/security/test/general/test_block_script_wrong_mime.html b/dom/security/test/general/test_block_script_wrong_mime.html
new file mode 100644
index 000000000..f4da9c577
--- /dev/null
+++ b/dom/security/test/general/test_block_script_wrong_mime.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1288361 - Block scripts with incorrect MIME type</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="text/javascript">
+
+const MIMETypes = [
+ ["application/javascript", true],
+ ["text/javascript", true],
+
+ ["audio/mpeg", false],
+ ["audio/", false],
+ ["image/jpeg", false],
+ ["image/", false],
+ ["video/mpeg", false],
+ ["video/", false],
+ ["text/csv", false],
+];
+
+// <script src="">
+function testScript([mime, shouldLoad]) {
+ return new Promise((resolve, reject) => {
+ let script = document.createElement("script");
+ script.onload = () => {
+ document.body.removeChild(script);
+ ok(shouldLoad, `script with mime '${mime}' should load`);
+ resolve();
+ };
+ script.onerror = () => {
+ document.body.removeChild(script);
+ ok(!shouldLoad, `script with wrong mime '${mime}' should be blocked`);
+ resolve();
+ };
+ script.src = "file_block_script_wrong_mime_server.sjs?type=script&mime="+mime;
+ document.body.appendChild(script);
+ });
+}
+
+// new Worker()
+function testWorker([mime, shouldLoad]) {
+ return new Promise((resolve, reject) => {
+ let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker&mime="+mime);
+ worker.onmessage = (event) => {
+ ok(shouldLoad, `worker with mime '${mime}' should load`)
+ is(event.data, "worker-loaded", "worker should send correct message");
+ resolve();
+ };
+ worker.onerror = (error) => {
+ ok(!shouldLoad, `worker with wrong mime '${mime}' should be blocked`);
+ let msg = error.message;
+ ok(msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/),
+ "should gets correct error message");
+ error.preventDefault();
+ resolve();
+ }
+ worker.postMessage("dummy");
+ });
+}
+
+// new Worker() with importScripts()
+function testWorkerImportScripts([mime, shouldLoad]) {
+ return new Promise((resolve, reject) => {
+ let worker = new Worker("file_block_script_wrong_mime_server.sjs?type=worker-import&mime="+mime);
+ worker.onmessage = (event) => {
+ ok(shouldLoad, `worker/importScripts with mime '${mime}' should load`)
+ is(event.data, "worker-loaded", "worker should send correct message");
+ resolve();
+ };
+ worker.onerror = (error) => {
+ ok(!shouldLoad, `worker/importScripts with wrong mime '${mime}' should be blocked`);
+ let msg = error.message;
+ ok(msg.match(/^NetworkError/) || msg.match(/Failed to load worker script/),
+ "should gets correct error message");
+ error.preventDefault();
+ resolve();
+ }
+ worker.postMessage("dummy");
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({set: [["security.block_script_with_wrong_mime", true]]}, function() {
+ Promise.all(MIMETypes.map(testScript)).then(() => {
+ return Promise.all(MIMETypes.map(testWorker));
+ }).then(() => {
+ return Promise.all(MIMETypes.map(testWorkerImportScripts));
+ }).then(() => {
+ SpecialPowers.popPrefEnv(SimpleTest.finish);
+ });
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/general/test_bug1277803.xul b/dom/security/test/general/test_bug1277803.xul
new file mode 100644
index 000000000..a62285f8a
--- /dev/null
+++ b/dom/security/test/general/test_bug1277803.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Bug 1277803 test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="600"
+ height="600"
+ onload="runTest();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.requestCompleteLog();
+ let Ci = Components.interfaces;
+ let Cc = Components.classes;
+ let Cu = Components.utils;
+ let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+ const BASE_URI = "http://mochi.test:8888/chrome/dom/security/test/general/";
+ const FAVICON_URI = BASE_URI + "favicon_bug1277803.ico";
+ const LOADING_URI = BASE_URI + "bug1277803.html";
+ let testWindow; //will be used to trigger favicon load
+
+ let securityManager = Cc["@mozilla.org/scriptsecuritymanager;1"].
+ getService(Ci.nsIScriptSecurityManager);
+ let expectedPrincipal = securityManager.createCodebasePrincipal(makeURI(LOADING_URI), {});
+ let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].createInstance();
+
+ // We expect 2 favicon loads, one from PlacesUIUtils.loadFavicon and one
+ // from XUL:image loads.
+ let requestXUL = false;
+ let requestPlaces = false;
+
+ function runTest() {
+ // Register our observer to intercept favicon requests.
+ let os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ let observer = {
+ observe: function(aSubject, aTopic, aData)
+ {
+ // Make sure this is a favicon request.
+ let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+ if (FAVICON_URI != httpChannel.URI.spec) {
+ return;
+ }
+
+ // Ensure the topic is the one we set an observer for.
+ is(aTopic, "http-on-modify-request", "Expected observer topic");
+
+ // Check for the correct loadingPrincipal, triggeringPrincipal.
+ let triggeringPrincipal = httpChannel.loadInfo.triggeringPrincipal;
+ let loadingPrincipal = httpChannel.loadInfo.loadingPrincipal;
+
+ if (loadingPrincipal.equals(systemPrincipal)) {
+ // This is the favicon loading from XUL, which will have the system
+ // principal as its loading principal and have a content principal
+ // as its triggering principal.
+ ok(triggeringPrincipal.equals(expectedPrincipal),
+ "Correct triggeringPrincipal for favicon from XUL.");
+ requestXUL = true;
+ } else if (loadingPrincipal.equals(expectedPrincipal)) {
+ // This is the favicon loading from Places, which will have a
+ // content principal as its loading principal and triggering
+ // principal.
+ ok(triggeringPrincipal.equals(expectedPrincipal),
+ "Correct triggeringPrincipal for favicon from Places.");
+ requestPlaces = true;
+ } else {
+ ok(false, "An unexpected favicon request.")
+ }
+
+ // Cleanup after ourselves...
+ if (requestXUL && requestPlaces) {
+ os.removeObserver(this, "http-on-modify-request");
+ SimpleTest.finish();
+ }
+ }
+ }
+ os.addObserver(observer, "http-on-modify-request", false);
+
+ // Now that the observer is set up, trigger a favicon load with navigation
+ testWindow = window.open(LOADING_URI);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(function() {
+ if (testWindow) {
+ testWindow.close();
+ }
+ });
+ ]]></script>
+
+ <browser type="content-primary" flex="1" id="content" src="about:blank"/>
+</window>
diff --git a/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html
new file mode 100644
index 000000000..7b1ab72dc
--- /dev/null
+++ b/dom/security/test/general/test_contentpolicytype_targeted_link_iframe.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1255240 - Test content policy types within content policies for targeted links in iframes</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+/* Description of the test:
+ * Let's load a link into a targeted iframe and make sure the content policy
+ * type used for content policy checks is of TYPE_SUBDOCUMENT.
+ */
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+const EXPECTED_CONTENT_TYPE = Ci.nsIContentPolicy.TYPE_SUBDOCUMENT;
+const EXPECTED_URL =
+ "http://mochi.test:8888/tests/dom/security/test/general/file_contentpolicytype_targeted_link_iframe.sjs?innerframe";
+const TEST_FRAME_URL =
+ "file_contentpolicytype_targeted_link_iframe.sjs?testframe";
+
+// ----- START Content Policy implementation for the test
+var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+
+const POLICYNAME = "@mozilla.org/iframetestpolicy;1";
+const POLICYID = SpecialPowers.wrap(SpecialPowers.Components)
+ .ID("{6cc95ef3-40e1-4d59-87f0-86f100373227}");
+
+var policy = {
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ iid = SpecialPowers.wrap(iid);
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIContentPolicy))
+ return this;
+
+ throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, iid) {
+ return this.QueryInterface(iid);
+ },
+
+ // nsIContentPolicy implementation
+ shouldLoad: function(contentType, contentLocation, requestOrigin,
+ context, mimeTypeGuess, extra) {
+
+ // make sure we get the right amount of content policy calls
+ // e.g. about:blank also gets chrcked by content policies
+ if (contentLocation.asciiSpec === EXPECTED_URL) {
+ is(contentType, EXPECTED_CONTENT_TYPE,
+ "content policy type should TYPESUBDOCUMENT");
+ categoryManager.deleteCategoryEntry("content-policy", POLICYNAME, false);
+ SimpleTest.finish();
+ }
+ return Ci.nsIContentPolicy.ACCEPT;
+ },
+
+ shouldProcess: function(contentType, contentLocation, requestOrigin,
+ context, mimeTypeGuess, extra) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+}
+policy = SpecialPowers.wrapCallbackObject(policy);
+
+// Register content policy
+var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager
+ .QueryInterface(Ci.nsIComponentRegistrar);
+
+componentManager.registerFactory(POLICYID, "Test content policy", POLICYNAME, policy);
+categoryManager.addCategoryEntry("content-policy", POLICYNAME, POLICYNAME, false, true);
+
+// ----- END Content Policy implementation for the test
+
+
+// start the test
+SimpleTest.waitForExplicitFinish();
+var testframe = document.getElementById("testframe");
+testframe.src = TEST_FRAME_URL;
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/general/test_nosniff.html b/dom/security/test/general/test_nosniff.html
new file mode 100644
index 000000000..197251e68
--- /dev/null
+++ b/dom/security/test/general/test_nosniff.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 471020 - Add X-Content-Type-Options: nosniff support to Firefox</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <!-- add the two css tests -->
+ <link rel="stylesheet" id="cssCorrectType">
+ <link rel="stylesheet" id="cssWrongType">
+</head>
+<body>
+
+<!-- add the two script tests -->
+<script id="scriptCorrectType"></script>
+<script id="scriptWrongType"></script>
+
+<!-- add the two img tests -->
+<img id="imgCorrectType" />
+<img id="imgWrongType" />
+
+<script class="testbody" type="text/javascript">
+/* Description of the test:
+ * We load 2 css files, 2 script files and 2 image files, where
+ * the sever either responds with the right mime type or
+ * the wrong mime type for each test.
+ */
+
+SimpleTest.waitForExplicitFinish();
+const NUM_TESTS = 6;
+
+var testCounter = 0;
+function checkFinish() {
+ testCounter++;
+ if (testCounter === NUM_TESTS) {
+ SimpleTest.finish();
+ }
+}
+
+SpecialPowers.pushPrefEnv({set: [["security.xcto_nosniff_block_images", true]]}, function() {
+
+ // 1) Test CSS with correct mime type
+ var cssCorrectType = document.getElementById("cssCorrectType");
+ cssCorrectType.onload = function() {
+ ok(true, "style nosniff correct type should load");
+ checkFinish();
+ }
+ cssCorrectType.onerror = function() {
+ ok(false, "style nosniff correct type should load");
+ checkFinish();
+ }
+ cssCorrectType.href = "file_nosniff_testserver.sjs?cssCorrectType";
+
+ // 2) Test CSS with wrong mime type
+ var cssWrongType = document.getElementById("cssWrongType");
+ cssWrongType.onload = function() {
+ ok(false, "style nosniff wrong type should not load");
+ checkFinish();
+ }
+ cssWrongType.onerror = function() {
+ ok(true, "style nosniff wrong type should not load");
+ checkFinish();
+ }
+ cssWrongType.href = "file_nosniff_testserver.sjs?cssWrongType";
+
+ // 3) Test SCRIPT with correct mime type
+ var scriptCorrectType = document.getElementById("scriptCorrectType");
+ scriptCorrectType.onload = function() {
+ ok(true, "script nosniff correct type should load");
+ checkFinish();
+ }
+ scriptCorrectType.onerror = function() {
+ ok(false, "script nosniff correct type should load");
+ checkFinish();
+ }
+ scriptCorrectType.src = "file_nosniff_testserver.sjs?scriptCorrectType";
+
+ // 4) Test SCRIPT with wrong mime type
+ var scriptWrongType = document.getElementById("scriptWrongType");
+ scriptWrongType.onload = function() {
+ ok(false, "script nosniff wrong type should not load");
+ checkFinish();
+ }
+ scriptWrongType.onerror = function() {
+ ok(true, "script nosniff wrong type should not load");
+ checkFinish();
+ }
+ scriptWrongType.src = "file_nosniff_testserver.sjs?scriptWrongType";
+
+ // 5) Test IMG with correct mime type
+ var imgCorrectType = document.getElementById("imgCorrectType");
+ imgCorrectType.onload = function() {
+ ok(true, "img nosniff correct type should load");
+ checkFinish();
+ }
+ imgCorrectType.onerror = function() {
+ ok(false, "img nosniff correct type should load");
+ checkFinish();
+ }
+ imgCorrectType.src = "file_nosniff_testserver.sjs?imgCorrectType";
+
+ // 6) Test IMG with wrong mime type
+ var imgWrongType = document.getElementById("imgWrongType");
+ imgWrongType.onload = function() {
+ ok(false, "img nosniff wrong type should not load");
+ checkFinish();
+ }
+ imgWrongType.onerror = function() {
+ ok(true, "img nosniff wrong type should not load");
+ checkFinish();
+ }
+ imgWrongType.src = "file_nosniff_testserver.sjs?imgWrongType";
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/gtest/TestCSPParser.cpp b/dom/security/test/gtest/TestCSPParser.cpp
new file mode 100644
index 000000000..fafa7b5d9
--- /dev/null
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -0,0 +1,1132 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifndef MOZILLA_INTERNAL_API
+// some of the includes make use of internal string types
+#define nsAString_h___
+#define nsString_h___
+#define nsStringFwd_h___
+#define nsReadableUtils_h___
+class nsACString;
+class nsAString;
+class nsAFlatString;
+class nsAFlatCString;
+class nsAdoptingString;
+class nsAdoptingCString;
+class nsXPIDLString;
+template<class T> class nsReadingIterator;
+#endif
+
+#include "nsIContentSecurityPolicy.h"
+#include "nsNetUtil.h"
+#include "nsIScriptSecurityManager.h"
+#include "mozilla/dom/nsCSPContext.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#undef nsString_h___
+#undef nsAString_h___
+#undef nsReadableUtils_h___
+#endif
+
+/*
+ * Testing the parser is non trivial, especially since we can not call
+ * parser functionality directly in compiled code tests.
+ * All the tests (except the fuzzy tests at the end) follow the same schemata:
+ * a) create an nsIContentSecurityPolicy object
+ * b) set the selfURI in SetRequestContext
+ * c) append one or more policies by calling AppendPolicy
+ * d) check if the policy count is correct by calling GetPolicyCount
+ * e) compare the result of the policy with the expected output
+ * using the struct PolicyTest;
+ *
+ * In general we test:
+ * a) policies that the parser should accept
+ * b) policies that the parser should reject
+ * c) policies that are randomly generated (fuzzy tests)
+ *
+ * Please note that fuzzy tests are *DISABLED* by default and shold only
+ * be run *OFFLINE* whenever code in nsCSPParser changes.
+ * To run fuzzy tests, flip RUN_OFFLINE_TESTS to 1.
+ *
+ */
+
+#define RUN_OFFLINE_TESTS 0
+
+/*
+ * Offline tests are separated in three different groups:
+ * * TestFuzzyPolicies - complete random ASCII input
+ * * TestFuzzyPoliciesIncDir - a directory name followed by random ASCII
+ * * TestFuzzyPoliciesIncDirLimASCII - a directory name followed by limited ASCII
+ * which represents more likely user input.
+ *
+ * We run each of this categories |kFuzzyRuns| times.
+ */
+
+#if RUN_OFFLINE_TESTS
+static const uint32_t kFuzzyRuns = 10000;
+#endif
+
+// For fuzzy testing we actually do not care about the output,
+// we just want to make sure that the parser can handle random
+// input, therefore we use kFuzzyExpectedPolicyCount to return early.
+static const uint32_t kFuzzyExpectedPolicyCount = 111;
+
+static const uint32_t kMaxPolicyLength = 96;
+
+struct PolicyTest
+{
+ char policy[kMaxPolicyLength];
+ char expectedResult[kMaxPolicyLength];
+};
+
+nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies which should fail to parse
+ const char* aPolicy,
+ const char* aExpectedResult) {
+
+ nsresult rv;
+ nsCOMPtr<nsIScriptSecurityManager> secman =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // we init the csp with http://www.selfuri.com
+ nsCOMPtr<nsIURI> selfURI;
+ rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> selfURIPrincipal;
+ // Can't use BasePrincipal::CreateCodebasePrincipal here
+ // because the symbol is not visible here
+ rv = secman->GetCodebasePrincipal(selfURI, getter_AddRefs(selfURIPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create a CSP object
+ nsCOMPtr<nsIContentSecurityPolicy> csp =
+ do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // for testing the parser we only need to set a principal which is needed
+ // to translate the keyword 'self' into an actual URI.
+ rv = csp->SetRequestContext(nullptr, selfURIPrincipal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // append a policy
+ nsString policyStr;
+ policyStr.AssignASCII(aPolicy);
+ rv = csp->AppendPolicy(policyStr, false, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // when executing fuzzy tests we do not care about the actual output
+ // of the parser, we just want to make sure that the parser is not crashing.
+ if (aExpectedPolicyCount == kFuzzyExpectedPolicyCount) {
+ return NS_OK;
+ }
+
+ // verify that the expected number of policies exists
+ uint32_t actualPolicyCount;
+ rv = csp->GetPolicyCount(&actualPolicyCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (actualPolicyCount != aExpectedPolicyCount) {
+ EXPECT_TRUE(false) <<
+ "Actual policy count not equal to expected policy count (" <<
+ actualPolicyCount << " != " << aExpectedPolicyCount <<
+ ") for policy: " << aPolicy;
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // if the expected policy count is 0, we can return, because
+ // we can not compare any output anyway. Used when parsing
+ // errornous policies.
+ if (aExpectedPolicyCount == 0) {
+ return NS_OK;
+ }
+
+ // compare the parsed policy against the expected result
+ nsString parsedPolicyStr;
+ // checking policy at index 0, which is the one what we appended.
+ rv = csp->GetPolicyString(0, parsedPolicyStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!NS_ConvertUTF16toUTF8(parsedPolicyStr).EqualsASCII(aExpectedResult)) {
+ EXPECT_TRUE(false) <<
+ "Actual policy does not match expected policy (" <<
+ NS_ConvertUTF16toUTF8(parsedPolicyStr).get() << " != " <<
+ aExpectedResult << ")";
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+// ============================= run Tests ========================
+
+nsresult runTestSuite(const PolicyTest* aPolicies,
+ uint32_t aPolicyCount,
+ uint32_t aExpectedPolicyCount) {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ bool experimentalEnabledCache = false;
+ bool strictDynamicEnabledCache = false;
+ if (prefs)
+ {
+ prefs->GetBoolPref("security.csp.experimentalEnabled", &experimentalEnabledCache);
+ prefs->SetBoolPref("security.csp.experimentalEnabled", true);
+
+ prefs->GetBoolPref("security.csp.enableStrictDynamic", &strictDynamicEnabledCache);
+ prefs->SetBoolPref("security.csp.enableStrictDynamic", true);
+ }
+
+ for (uint32_t i = 0; i < aPolicyCount; i++) {
+ rv = runTest(aExpectedPolicyCount, aPolicies[i].policy, aPolicies[i].expectedResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (prefs) {
+ prefs->SetBoolPref("security.csp.experimentalEnabled", experimentalEnabledCache);
+ prefs->SetBoolPref("security.csp.enableStrictDynamic", strictDynamicEnabledCache);
+ }
+
+ return NS_OK;
+}
+
+// ============================= TestDirectives ========================
+
+TEST(CSPParser, Directives)
+{
+ static const PolicyTest policies[] =
+ {
+ { "default-src http://www.example.com",
+ "default-src http://www.example.com" },
+ { "script-src http://www.example.com",
+ "script-src http://www.example.com" },
+ { "object-src http://www.example.com",
+ "object-src http://www.example.com" },
+ { "style-src http://www.example.com",
+ "style-src http://www.example.com" },
+ { "img-src http://www.example.com",
+ "img-src http://www.example.com" },
+ { "media-src http://www.example.com",
+ "media-src http://www.example.com" },
+ { "frame-src http://www.example.com",
+ "frame-src http://www.example.com" },
+ { "font-src http://www.example.com",
+ "font-src http://www.example.com" },
+ { "connect-src http://www.example.com",
+ "connect-src http://www.example.com" },
+ { "report-uri http://www.example.com",
+ "report-uri http://www.example.com/" },
+ { "script-src 'nonce-correctscriptnonce'",
+ "script-src 'nonce-correctscriptnonce'" },
+ { "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
+ "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+ { "referrer no-referrer",
+ "referrer no-referrer" },
+ { "require-sri-for script style",
+ "require-sri-for script style"},
+ { "script-src 'nonce-foo' 'unsafe-inline' ",
+ "script-src 'nonce-foo' 'unsafe-inline'" },
+ { "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https: ",
+ "script-src 'nonce-foo' 'strict-dynamic' 'unsafe-inline' https:" },
+ { "default-src 'sha256-siVR8' 'strict-dynamic' 'unsafe-inline' https: ",
+ "default-src 'sha256-siVR8' 'unsafe-inline' https:" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestKeywords ========================
+
+TEST(CSPParser, Keywords)
+{
+ static const PolicyTest policies[] =
+ {
+ { "script-src 'self'",
+ "script-src http://www.selfuri.com" },
+ { "script-src 'unsafe-inline'",
+ "script-src 'unsafe-inline'" },
+ { "script-src 'unsafe-eval'",
+ "script-src 'unsafe-eval'" },
+ { "script-src 'unsafe-inline' 'unsafe-eval'",
+ "script-src 'unsafe-inline' 'unsafe-eval'" },
+ { "script-src 'none'",
+ "script-src 'none'" },
+ { "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src 'self'",
+ "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src http://www.selfuri.com" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestIgnoreUpperLowerCasePolicies ========================
+
+TEST(CSPParser, IgnoreUpperLowerCasePolicies)
+{
+ static const PolicyTest policies[] =
+ {
+ { "script-src 'SELF'",
+ "script-src http://www.selfuri.com" },
+ { "sCriPt-src 'Unsafe-Inline'",
+ "script-src 'unsafe-inline'" },
+ { "SCRIPT-src 'unsafe-eval'",
+ "script-src 'unsafe-eval'" },
+ { "default-SRC 'unsafe-inline' 'unsafe-eval'",
+ "default-src 'unsafe-inline' 'unsafe-eval'" },
+ { "script-src 'NoNe'",
+ "script-src 'none'" },
+ { "img-sRc 'noNe'; scrIpt-src 'unsafe-EVAL' 'UNSAFE-inline'; deFAULT-src 'Self'",
+ "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src http://www.selfuri.com" },
+ { "default-src HTTP://www.example.com",
+ "default-src http://www.example.com" },
+ { "default-src HTTP://WWW.EXAMPLE.COM",
+ "default-src http://www.example.com" },
+ { "default-src HTTPS://*.example.COM",
+ "default-src https://*.example.com" },
+ { "script-src 'none' test.com;",
+ "script-src http://test.com" },
+ { "script-src 'NoNCE-correctscriptnonce'",
+ "script-src 'nonce-correctscriptnonce'" },
+ { "script-src 'NoncE-NONCENEEDSTOBEUPPERCASE'",
+ "script-src 'nonce-NONCENEEDSTOBEUPPERCASE'" },
+ { "script-src 'SHA256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
+ "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+ { "refERRer No-refeRRer",
+ "referrer no-referrer" },
+ { "upgrade-INSECURE-requests",
+ "upgrade-insecure-requests" },
+ { "sanDBox alloW-foRMs",
+ "sandbox allow-forms"},
+ { "require-SRI-for sCript stYle",
+ "require-sri-for script style"},
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestPaths ========================
+
+TEST(CSPParser, Paths)
+{
+ static const PolicyTest policies[] =
+ {
+ { "script-src http://www.example.com",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com/",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/path-1",
+ "script-src http://www.example.com/path-1" },
+ { "script-src http://www.example.com/path-1/",
+ "script-src http://www.example.com/path-1/" },
+ { "script-src http://www.example.com/path-1/path_2",
+ "script-src http://www.example.com/path-1/path_2" },
+ { "script-src http://www.example.com/path-1/path_2/",
+ "script-src http://www.example.com/path-1/path_2/" },
+ { "script-src http://www.example.com/path-1/path_2/file.js",
+ "script-src http://www.example.com/path-1/path_2/file.js" },
+ { "script-src http://www.example.com/path-1/path_2/file_1.js",
+ "script-src http://www.example.com/path-1/path_2/file_1.js" },
+ { "script-src http://www.example.com/path-1/path_2/file-2.js",
+ "script-src http://www.example.com/path-1/path_2/file-2.js" },
+ { "script-src http://www.example.com/path-1/path_2/f.js",
+ "script-src http://www.example.com/path-1/path_2/f.js" },
+ { "script-src http://www.example.com:88",
+ "script-src http://www.example.com:88" },
+ { "script-src http://www.example.com:88/",
+ "script-src http://www.example.com:88/" },
+ { "script-src http://www.example.com:88/path-1",
+ "script-src http://www.example.com:88/path-1" },
+ { "script-src http://www.example.com:88/path-1/",
+ "script-src http://www.example.com:88/path-1/" },
+ { "script-src http://www.example.com:88/path-1/path_2",
+ "script-src http://www.example.com:88/path-1/path_2" },
+ { "script-src http://www.example.com:88/path-1/path_2/",
+ "script-src http://www.example.com:88/path-1/path_2/" },
+ { "script-src http://www.example.com:88/path-1/path_2/file.js",
+ "script-src http://www.example.com:88/path-1/path_2/file.js" },
+ { "script-src http://www.example.com:*",
+ "script-src http://www.example.com:*" },
+ { "script-src http://www.example.com:*/",
+ "script-src http://www.example.com:*/" },
+ { "script-src http://www.example.com:*/path-1",
+ "script-src http://www.example.com:*/path-1" },
+ { "script-src http://www.example.com:*/path-1/",
+ "script-src http://www.example.com:*/path-1/" },
+ { "script-src http://www.example.com:*/path-1/path_2",
+ "script-src http://www.example.com:*/path-1/path_2" },
+ { "script-src http://www.example.com:*/path-1/path_2/",
+ "script-src http://www.example.com:*/path-1/path_2/" },
+ { "script-src http://www.example.com:*/path-1/path_2/file.js",
+ "script-src http://www.example.com:*/path-1/path_2/file.js" },
+ { "script-src http://www.example.com#foo",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com?foo=bar",
+ "script-src http://www.example.com" },
+ { "script-src http://www.example.com:8888#foo",
+ "script-src http://www.example.com:8888" },
+ { "script-src http://www.example.com:8888?foo",
+ "script-src http://www.example.com:8888" },
+ { "script-src http://www.example.com/#foo",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/?foo",
+ "script-src http://www.example.com/" },
+ { "script-src http://www.example.com/path-1/file.js#foo",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "script-src http://www.example.com/path-1/file.js?foo",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "script-src http://www.example.com/path-1/file.js?foo#bar",
+ "script-src http://www.example.com/path-1/file.js" },
+ { "report-uri http://www.example.com/",
+ "report-uri http://www.example.com/" },
+ { "report-uri http://www.example.com:8888/asdf",
+ "report-uri http://www.example.com:8888/asdf" },
+ { "report-uri http://www.example.com:8888/path_1/path_2",
+ "report-uri http://www.example.com:8888/path_1/path_2" },
+ { "report-uri http://www.example.com:8888/path_1/path_2/report.sjs&301",
+ "report-uri http://www.example.com:8888/path_1/path_2/report.sjs&301" },
+ { "report-uri /examplepath",
+ "report-uri http://www.selfuri.com/examplepath" },
+ { "connect-src http://www.example.com/foo%3Bsessionid=12%2C34",
+ "connect-src http://www.example.com/foo;sessionid=12,34" },
+ { "connect-src http://www.example.com/foo%3bsessionid=12%2c34",
+ "connect-src http://www.example.com/foo;sessionid=12,34" },
+ { "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@",
+ "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@" },
+ { "script-src http://www.example.com:88/.js",
+ "script-src http://www.example.com:88/.js" },
+ { "script-src https://foo.com/_abc/abc_/_/_a_b_c_",
+ "script-src https://foo.com/_abc/abc_/_/_a_b_c_" }
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestSimplePolicies ========================
+
+TEST(CSPParser, SimplePolicies)
+{
+ static const PolicyTest policies[] =
+ {
+ { "default-src *",
+ "default-src *" },
+ { "default-src https:",
+ "default-src https:" },
+ { "default-src https://*",
+ "default-src https://*" },
+ { "default-src *:*",
+ "default-src http://*:*" },
+ { "default-src *:80",
+ "default-src http://*:80" },
+ { "default-src http://*:80",
+ "default-src http://*:80" },
+ { "default-src javascript:",
+ "default-src javascript:" },
+ { "default-src data:",
+ "default-src data:" },
+ { "script-src 'unsafe-eval' 'unsafe-inline' http://www.example.com",
+ "script-src 'unsafe-eval' 'unsafe-inline' http://www.example.com" },
+ { "object-src 'self'",
+ "object-src http://www.selfuri.com" },
+ { "style-src http://www.example.com 'self'",
+ "style-src http://www.example.com http://www.selfuri.com" },
+ { "media-src http://www.example.com http://www.test.com",
+ "media-src http://www.example.com http://www.test.com" },
+ { "connect-src http://www.test.com example.com *.other.com;",
+ "connect-src http://www.test.com http://example.com http://*.other.com"},
+ { "connect-src example.com *.other.com",
+ "connect-src http://example.com http://*.other.com"},
+ { "style-src *.other.com example.com",
+ "style-src http://*.other.com http://example.com"},
+ { "default-src 'self'; img-src *;",
+ "default-src http://www.selfuri.com; img-src *" },
+ { "object-src media1.example.com media2.example.com *.cdn.example.com;",
+ "object-src http://media1.example.com http://media2.example.com http://*.cdn.example.com" },
+ { "script-src trustedscripts.example.com",
+ "script-src http://trustedscripts.example.com" },
+ { "script-src 'self' ; default-src trustedscripts.example.com",
+ "script-src http://www.selfuri.com; default-src http://trustedscripts.example.com" },
+ { "default-src 'none'; report-uri http://localhost:49938/test",
+ "default-src 'none'; report-uri http://localhost:49938/test" },
+ { "default-src app://{app-host-is-uid}",
+ "default-src app://{app-host-is-uid}" },
+ { " ; default-src abc",
+ "default-src http://abc" },
+ { " ; ; ; ; default-src abc ; ; ; ;",
+ "default-src http://abc" },
+ { "script-src 'none' 'none' 'none';",
+ "script-src 'none'" },
+ { "script-src http://www.example.com/path-1//",
+ "script-src http://www.example.com/path-1//" },
+ { "script-src http://www.example.com/path-1//path_2",
+ "script-src http://www.example.com/path-1//path_2" },
+ { "default-src 127.0.0.1",
+ "default-src http://127.0.0.1" },
+ { "default-src 127.0.0.1:*",
+ "default-src http://127.0.0.1:*" },
+ { "default-src -; ",
+ "default-src http://-" },
+ { "script-src 1",
+ "script-src http://1" },
+ { "upgrade-insecure-requests",
+ "upgrade-insecure-requests" },
+ { "upgrade-insecure-requests https:",
+ "upgrade-insecure-requests" },
+ { "sandbox allow-scripts allow-forms ",
+ "sandbox allow-scripts allow-forms" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestPoliciesWithInvalidSrc ========================
+
+TEST(CSPParser, PoliciesWithInvalidSrc)
+{
+ static const PolicyTest policies[] =
+ {
+ { "script-src 'self'; SCRIPT-SRC http://www.example.com",
+ "script-src http://www.selfuri.com" },
+ { "script-src 'none' test.com; script-src example.com",
+ "script-src http://test.com" },
+ { "default-src **",
+ "default-src 'none'" },
+ { "default-src 'self",
+ "default-src 'none'" },
+ { "default-src 'unsafe-inlin' ",
+ "default-src 'none'" },
+ { "default-src */",
+ "default-src 'none'" },
+ { "default-src",
+ "default-src 'none'" },
+ { "default-src 'unsafe-inlin' ",
+ "default-src 'none'" },
+ { "default-src :88",
+ "default-src 'none'" },
+ { "script-src abc::::::88",
+ "script-src 'none'" },
+ { "script-src *.*:*",
+ "script-src 'none'" },
+ { "img-src *::88",
+ "img-src 'none'" },
+ { "object-src http://localhost:",
+ "object-src 'none'" },
+ { "script-src test..com",
+ "script-src 'none'" },
+ { "script-src sub1.sub2.example+",
+ "script-src 'none'" },
+ { "script-src http://www.example.com//",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88path-1/",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//path-1",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88//path-1",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:88.js",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:*.js",
+ "script-src 'none'" },
+ { "script-src http://www.example.com:*.",
+ "script-src 'none'" },
+ { "connect-src http://www.example.com/foo%zz;",
+ "connect-src 'none'" },
+ { "script-src https://foo.com/%$",
+ "script-src 'none'" },
+ { "require-SRI-for script elephants",
+ "require-sri-for script"},
+ { "sandbox foo",
+ "sandbox"},
+ };
+
+ // amount of tests - 1, because the latest should be ignored.
+ uint32_t policyCount = (sizeof(policies) / sizeof(PolicyTest)) -1;
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestBadPolicies ========================
+
+TEST(CSPParser, BadPolicies)
+{
+ static const PolicyTest policies[] =
+ {
+ { "script-sr 'self", "" },
+ { "", "" },
+ { "; ; ; ; ; ; ;", "" },
+ { "defaut-src asdf", "" },
+ { "default-src: aaa", "" },
+ { "asdf http://test.com", ""},
+ { "referrer", ""},
+ { "referrer foo", ""},
+ { "require-sri-for", ""},
+ { "require-sri-for foo", ""},
+ { "report-uri", ""},
+ { "report-uri http://:foo", ""},
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 0)));
+}
+
+// ============================= TestGoodGeneratedPolicies ========================
+
+TEST(CSPParser, GoodGeneratedPolicies)
+{
+ static const PolicyTest policies[] =
+ {
+ { "default-src 'self'; img-src *",
+ "default-src http://www.selfuri.com; img-src *" },
+ { "report-uri /policy",
+ "report-uri http://www.selfuri.com/policy"},
+ { "img-src *",
+ "img-src *" },
+ { "media-src foo.bar",
+ "media-src http://foo.bar" },
+ { "frame-src *.bar",
+ "frame-src http://*.bar" },
+ { "font-src com",
+ "font-src http://com" },
+ { "connect-src f00b4r.com",
+ "connect-src http://f00b4r.com" },
+ { "default-src {app-url-is-uid}",
+ "default-src http://{app-url-is-uid}" },
+ { "script-src *.a.b.c",
+ "script-src http://*.a.b.c" },
+ { "object-src *.b.c",
+ "object-src http://*.b.c" },
+ { "style-src a.b.c",
+ "style-src http://a.b.c" },
+ { "img-src a.com",
+ "img-src http://a.com" },
+ { "media-src http://abc.com",
+ "media-src http://abc.com" },
+ { "frame-src a2-c.com",
+ "frame-src http://a2-c.com" },
+ { "font-src https://a.com",
+ "font-src https://a.com" },
+ { "connect-src *.a.com",
+ "connect-src http://*.a.com" },
+ { "default-src a.com:23",
+ "default-src http://a.com:23" },
+ { "script-src https://a.com:200",
+ "script-src https://a.com:200" },
+ { "object-src data:",
+ "object-src data:" },
+ { "style-src javascript:",
+ "style-src javascript:" },
+ { "img-src {app-host-is-uid}",
+ "img-src http://{app-host-is-uid}" },
+ { "media-src app://{app-host-is-uid}",
+ "media-src app://{app-host-is-uid}" },
+ { "frame-src https://foobar.com:443",
+ "frame-src https://foobar.com:443" },
+ { "font-src https://a.com:443",
+ "font-src https://a.com:443" },
+ { "connect-src http://a.com:80",
+ "connect-src http://a.com:80" },
+ { "default-src http://foobar.com",
+ "default-src http://foobar.com" },
+ { "script-src https://foobar.com",
+ "script-src https://foobar.com" },
+ { "object-src https://{app-host-is-uid}",
+ "object-src https://{app-host-is-uid}" },
+ { "style-src 'none'",
+ "style-src 'none'" },
+ { "img-src foo.bar:21 https://ras.bar",
+ "img-src http://foo.bar:21 https://ras.bar" },
+ { "media-src http://foo.bar:21 https://ras.bar:443",
+ "media-src http://foo.bar:21 https://ras.bar:443" },
+ { "frame-src http://self.com:80",
+ "frame-src http://self.com:80" },
+ { "font-src http://self.com",
+ "font-src http://self.com" },
+ { "connect-src https://foo.com http://bar.com:88",
+ "connect-src https://foo.com http://bar.com:88" },
+ { "default-src * https://bar.com 'none'",
+ "default-src * https://bar.com" },
+ { "script-src *.foo.com",
+ "script-src http://*.foo.com" },
+ { "object-src http://b.com",
+ "object-src http://b.com" },
+ { "style-src http://bar.com:88",
+ "style-src http://bar.com:88" },
+ { "img-src https://bar.com:88",
+ "img-src https://bar.com:88" },
+ { "media-src http://bar.com:443",
+ "media-src http://bar.com:443" },
+ { "frame-src https://foo.com:88",
+ "frame-src https://foo.com:88" },
+ { "font-src http://foo.com",
+ "font-src http://foo.com" },
+ { "connect-src http://x.com:23",
+ "connect-src http://x.com:23" },
+ { "default-src http://barbaz.com",
+ "default-src http://barbaz.com" },
+ { "script-src http://somerandom.foo.com",
+ "script-src http://somerandom.foo.com" },
+ { "default-src *",
+ "default-src *" },
+ { "style-src http://bar.com:22",
+ "style-src http://bar.com:22" },
+ { "img-src https://foo.com:443",
+ "img-src https://foo.com:443" },
+ { "script-src https://foo.com; ",
+ "script-src https://foo.com" },
+ { "img-src bar.com:*",
+ "img-src http://bar.com:*" },
+ { "font-src https://foo.com:400",
+ "font-src https://foo.com:400" },
+ { "connect-src http://bar.com:400",
+ "connect-src http://bar.com:400" },
+ { "default-src http://evil.com",
+ "default-src http://evil.com" },
+ { "script-src https://evil.com:100",
+ "script-src https://evil.com:100" },
+ { "default-src bar.com; script-src https://foo.com",
+ "default-src http://bar.com; script-src https://foo.com" },
+ { "default-src 'self'; script-src 'self' https://*:*",
+ "default-src http://www.selfuri.com; script-src http://www.selfuri.com https://*:*" },
+ { "img-src http://self.com:34",
+ "img-src http://self.com:34" },
+ { "media-src http://subd.self.com:34",
+ "media-src http://subd.self.com:34" },
+ { "default-src 'none'",
+ "default-src 'none'" },
+ { "connect-src http://self",
+ "connect-src http://self" },
+ { "default-src http://foo",
+ "default-src http://foo" },
+ { "script-src http://foo:80",
+ "script-src http://foo:80" },
+ { "object-src http://bar",
+ "object-src http://bar" },
+ { "style-src http://three:80",
+ "style-src http://three:80" },
+ { "img-src https://foo:400",
+ "img-src https://foo:400" },
+ { "media-src https://self:34",
+ "media-src https://self:34" },
+ { "frame-src https://bar",
+ "frame-src https://bar" },
+ { "font-src http://three:81",
+ "font-src http://three:81" },
+ { "connect-src https://three:81",
+ "connect-src https://three:81" },
+ { "script-src http://self.com:80/foo",
+ "script-src http://self.com:80/foo" },
+ { "object-src http://self.com/foo",
+ "object-src http://self.com/foo" },
+ { "report-uri /report.py",
+ "report-uri http://www.selfuri.com/report.py"},
+ { "img-src http://foo.org:34/report.py",
+ "img-src http://foo.org:34/report.py" },
+ { "media-src foo/bar/report.py",
+ "media-src http://foo/bar/report.py" },
+ { "report-uri /",
+ "report-uri http://www.selfuri.com/"},
+ { "font-src https://self.com/report.py",
+ "font-src https://self.com/report.py" },
+ { "connect-src https://foo.com/report.py",
+ "connect-src https://foo.com/report.py" },
+ { "default-src *; report-uri http://www.reporturi.com/",
+ "default-src *; report-uri http://www.reporturi.com/" },
+ { "default-src http://first.com",
+ "default-src http://first.com" },
+ { "script-src http://second.com",
+ "script-src http://second.com" },
+ { "object-src http://third.com",
+ "object-src http://third.com" },
+ { "style-src https://foobar.com:4443",
+ "style-src https://foobar.com:4443" },
+ { "img-src http://foobar.com:4443",
+ "img-src http://foobar.com:4443" },
+ { "media-src bar.com",
+ "media-src http://bar.com" },
+ { "frame-src http://bar.com",
+ "frame-src http://bar.com" },
+ { "font-src http://self.com/",
+ "font-src http://self.com/" },
+ { "script-src 'self'",
+ "script-src http://www.selfuri.com" },
+ { "default-src http://self.com/foo.png",
+ "default-src http://self.com/foo.png" },
+ { "script-src http://self.com/foo.js",
+ "script-src http://self.com/foo.js" },
+ { "object-src http://bar.com/foo.js",
+ "object-src http://bar.com/foo.js" },
+ { "style-src http://FOO.COM",
+ "style-src http://foo.com" },
+ { "img-src HTTP",
+ "img-src http://http" },
+ { "media-src http",
+ "media-src http://http" },
+ { "frame-src 'SELF'",
+ "frame-src http://www.selfuri.com" },
+ { "DEFAULT-src 'self';",
+ "default-src http://www.selfuri.com" },
+ { "default-src 'self' http://FOO.COM",
+ "default-src http://www.selfuri.com http://foo.com" },
+ { "default-src 'self' HTTP://foo.com",
+ "default-src http://www.selfuri.com http://foo.com" },
+ { "default-src 'NONE'",
+ "default-src 'none'" },
+ { "script-src policy-uri ",
+ "script-src http://policy-uri" },
+ { "img-src 'self'; ",
+ "img-src http://www.selfuri.com" },
+ { "frame-ancestors foo-bar.com",
+ "frame-ancestors http://foo-bar.com" },
+ { "frame-ancestors http://a.com",
+ "frame-ancestors http://a.com" },
+ { "frame-ancestors 'self'",
+ "frame-ancestors http://www.selfuri.com" },
+ { "frame-ancestors http://self.com:88",
+ "frame-ancestors http://self.com:88" },
+ { "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com",
+ "frame-ancestors http://a.b.c.d.e.f.g.h.i.j.k.l.x.com" },
+ { "frame-ancestors https://self.com:34",
+ "frame-ancestors https://self.com:34" },
+ { "default-src 'none'; frame-ancestors 'self'",
+ "default-src 'none'; frame-ancestors http://www.selfuri.com" },
+ { "frame-ancestors http://self:80",
+ "frame-ancestors http://self:80" },
+ { "frame-ancestors http://self.com/bar",
+ "frame-ancestors http://self.com/bar" },
+ { "default-src 'self'; frame-ancestors 'self'",
+ "default-src http://www.selfuri.com; frame-ancestors http://www.selfuri.com" },
+ { "frame-ancestors http://bar.com/foo.png",
+ "frame-ancestors http://bar.com/foo.png" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestBadGeneratedPolicies ========================
+
+TEST(CSPParser, BadGeneratedPolicies)
+{
+ static const PolicyTest policies[] =
+ {
+ { "foo.*.bar", ""},
+ { "foo!bar.com", ""},
+ { "x.*.a.com", ""},
+ { "a#2-c.com", ""},
+ { "http://foo.com:bar.com:23", ""},
+ { "f!oo.bar", ""},
+ { "ht!ps://f-oo.bar", ""},
+ { "https://f-oo.bar:3f", ""},
+ { "**", ""},
+ { "*a", ""},
+ { "http://username:password@self.com/foo", ""},
+ { "http://other:pass1@self.com/foo", ""},
+ { "http://user1:pass1@self.com/foo", ""},
+ { "http://username:password@self.com/bar", ""},
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 0)));
+}
+
+// ============ TestGoodGeneratedPoliciesForPathHandling ============
+
+TEST(CSPParser, GoodGeneratedPoliciesForPathHandling)
+{
+ // Once bug 808292 (Implement path-level host-source matching to CSP)
+ // lands we have to update the expected output to include the parsed path
+
+ static const PolicyTest policies[] =
+ {
+ { "img-src http://test1.example.com",
+ "img-src http://test1.example.com" },
+ { "img-src http://test1.example.com/",
+ "img-src http://test1.example.com/" },
+ { "img-src http://test1.example.com/path-1",
+ "img-src http://test1.example.com/path-1" },
+ { "img-src http://test1.example.com/path-1/",
+ "img-src http://test1.example.com/path-1/" },
+ { "img-src http://test1.example.com/path-1/path_2/",
+ "img-src http://test1.example.com/path-1/path_2/" },
+ { "img-src http://test1.example.com/path-1/path_2/file.js",
+ "img-src http://test1.example.com/path-1/path_2/file.js" },
+ { "img-src http://test1.example.com/path-1/path_2/file_1.js",
+ "img-src http://test1.example.com/path-1/path_2/file_1.js" },
+ { "img-src http://test1.example.com/path-1/path_2/file-2.js",
+ "img-src http://test1.example.com/path-1/path_2/file-2.js" },
+ { "img-src http://test1.example.com/path-1/path_2/f.js",
+ "img-src http://test1.example.com/path-1/path_2/f.js" },
+ { "img-src http://test1.example.com/path-1/path_2/f.oo.js",
+ "img-src http://test1.example.com/path-1/path_2/f.oo.js" },
+ { "img-src test1.example.com",
+ "img-src http://test1.example.com" },
+ { "img-src test1.example.com/",
+ "img-src http://test1.example.com/" },
+ { "img-src test1.example.com/path-1",
+ "img-src http://test1.example.com/path-1" },
+ { "img-src test1.example.com/path-1/",
+ "img-src http://test1.example.com/path-1/" },
+ { "img-src test1.example.com/path-1/path_2/",
+ "img-src http://test1.example.com/path-1/path_2/" },
+ { "img-src test1.example.com/path-1/path_2/file.js",
+ "img-src http://test1.example.com/path-1/path_2/file.js" },
+ { "img-src test1.example.com/path-1/path_2/file_1.js",
+ "img-src http://test1.example.com/path-1/path_2/file_1.js" },
+ { "img-src test1.example.com/path-1/path_2/file-2.js",
+ "img-src http://test1.example.com/path-1/path_2/file-2.js" },
+ { "img-src test1.example.com/path-1/path_2/f.js",
+ "img-src http://test1.example.com/path-1/path_2/f.js" },
+ { "img-src test1.example.com/path-1/path_2/f.oo.js",
+ "img-src http://test1.example.com/path-1/path_2/f.oo.js" },
+ { "img-src *.example.com",
+ "img-src http://*.example.com" },
+ { "img-src *.example.com/",
+ "img-src http://*.example.com/" },
+ { "img-src *.example.com/path-1",
+ "img-src http://*.example.com/path-1" },
+ { "img-src *.example.com/path-1/",
+ "img-src http://*.example.com/path-1/" },
+ { "img-src *.example.com/path-1/path_2/",
+ "img-src http://*.example.com/path-1/path_2/" },
+ { "img-src *.example.com/path-1/path_2/file.js",
+ "img-src http://*.example.com/path-1/path_2/file.js" },
+ { "img-src *.example.com/path-1/path_2/file_1.js",
+ "img-src http://*.example.com/path-1/path_2/file_1.js" },
+ { "img-src *.example.com/path-1/path_2/file-2.js",
+ "img-src http://*.example.com/path-1/path_2/file-2.js" },
+ { "img-src *.example.com/path-1/path_2/f.js",
+ "img-src http://*.example.com/path-1/path_2/f.js" },
+ { "img-src *.example.com/path-1/path_2/f.oo.js",
+ "img-src http://*.example.com/path-1/path_2/f.oo.js" },
+ { "img-src test1.example.com:80",
+ "img-src http://test1.example.com:80" },
+ { "img-src test1.example.com:80/",
+ "img-src http://test1.example.com:80/" },
+ { "img-src test1.example.com:80/path-1",
+ "img-src http://test1.example.com:80/path-1" },
+ { "img-src test1.example.com:80/path-1/",
+ "img-src http://test1.example.com:80/path-1/" },
+ { "img-src test1.example.com:80/path-1/path_2",
+ "img-src http://test1.example.com:80/path-1/path_2" },
+ { "img-src test1.example.com:80/path-1/path_2/",
+ "img-src http://test1.example.com:80/path-1/path_2/" },
+ { "img-src test1.example.com:80/path-1/path_2/file.js",
+ "img-src http://test1.example.com:80/path-1/path_2/file.js" },
+ { "img-src test1.example.com:80/path-1/path_2/f.ile.js",
+ "img-src http://test1.example.com:80/path-1/path_2/f.ile.js" },
+ { "img-src test1.example.com:*",
+ "img-src http://test1.example.com:*" },
+ { "img-src test1.example.com:*/",
+ "img-src http://test1.example.com:*/" },
+ { "img-src test1.example.com:*/path-1",
+ "img-src http://test1.example.com:*/path-1" },
+ { "img-src test1.example.com:*/path-1/",
+ "img-src http://test1.example.com:*/path-1/" },
+ { "img-src test1.example.com:*/path-1/path_2",
+ "img-src http://test1.example.com:*/path-1/path_2" },
+ { "img-src test1.example.com:*/path-1/path_2/",
+ "img-src http://test1.example.com:*/path-1/path_2/" },
+ { "img-src test1.example.com:*/path-1/path_2/file.js",
+ "img-src http://test1.example.com:*/path-1/path_2/file.js" },
+ { "img-src test1.example.com:*/path-1/path_2/f.ile.js",
+ "img-src http://test1.example.com:*/path-1/path_2/f.ile.js" },
+ { "img-src http://test1.example.com/abc//",
+ "img-src http://test1.example.com/abc//" },
+ { "img-src https://test1.example.com/abc/def//",
+ "img-src https://test1.example.com/abc/def//" },
+ { "img-src https://test1.example.com/abc/def/ghi//",
+ "img-src https://test1.example.com/abc/def/ghi//" },
+ { "img-src http://test1.example.com:80/abc//",
+ "img-src http://test1.example.com:80/abc//" },
+ { "img-src https://test1.example.com:80/abc/def//",
+ "img-src https://test1.example.com:80/abc/def//" },
+ { "img-src https://test1.example.com:80/abc/def/ghi//",
+ "img-src https://test1.example.com:80/abc/def/ghi//" },
+ { "img-src https://test1.example.com/abc////////////def/",
+ "img-src https://test1.example.com/abc////////////def/" },
+ { "img-src https://test1.example.com/abc////////////",
+ "img-src https://test1.example.com/abc////////////" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============ TestBadGeneratedPoliciesForPathHandling ============
+
+TEST(CSPParser, BadGeneratedPoliciesForPathHandling)
+{
+ static const PolicyTest policies[] =
+ {
+ { "img-src test1.example.com:88path-1/",
+ "img-src 'none'" },
+ { "img-src test1.example.com:80.js",
+ "img-src 'none'" },
+ { "img-src test1.example.com:*.js",
+ "img-src 'none'" },
+ { "img-src test1.example.com:*.",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com//",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com:80//",
+ "img-src 'none'" },
+ { "img-src http://test1.example.com:80abc",
+ "img-src 'none'" },
+ };
+
+ uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
+}
+
+// ============================= TestFuzzyPolicies ========================
+
+// Use a policy, eliminate one character at a time,
+// and feed it as input to the parser.
+
+TEST(CSPParser, ShorteningPolicies)
+{
+ char pol[] = "default-src http://www.sub1.sub2.example.com:88/path1/path2/ 'unsafe-inline' 'none'";
+ uint32_t len = static_cast<uint32_t>(sizeof(pol));
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+
+ while (--len) {
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+ memcpy(&testPol[0].policy, &pol, len * sizeof(char));
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+ kFuzzyExpectedPolicyCount)));
+ }
+}
+
+// ============================= TestFuzzyPolicies ========================
+
+// We generate kFuzzyRuns inputs by (pseudo) randomly picking from the 128
+// ASCII characters; feed them to the parser and verfy that the parser
+// handles the input gracefully.
+//
+// Please note, that by using srand(0) we get deterministic results!
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPolicies)
+{
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % kMaxPolicyLength;
+ // reset memory of the policy string
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
+
+ for (uint32_t i = 0; i < polLength; i++) {
+ // fill the policy array with random ASCII chars
+ testPol[0].policy[i] = static_cast<char>(rand() % 128);
+ }
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+ kFuzzyExpectedPolicyCount)));
+ }
+}
+
+#endif
+
+// ============================= TestFuzzyPoliciesIncDir ========================
+
+// In a similar fashion as in TestFuzzyPolicies, we again (pseudo) randomly
+// generate input for the parser, but this time also include a valid directive
+// followed by the random input.
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPoliciesIncDir)
+{
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ char defaultSrc[] = "default-src ";
+ int defaultSrcLen = sizeof(defaultSrc) - 1;
+ // copy default-src into the policy array
+ memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % (kMaxPolicyLength - defaultSrcLen);
+ // reset memory of the policy string, but leave default-src.
+ memset((&(testPol[0].policy) + (defaultSrcLen * sizeof(char))),
+ '\0', (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
+
+ // do not start at index 0 so we do not overwrite 'default-src'
+ for (uint32_t i = defaultSrcLen; i < polLength; i++) {
+ // fill the policy array with random ASCII chars
+ testPol[0].policy[i] = static_cast<char>(rand() % 128);
+ }
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+ kFuzzyExpectedPolicyCount)));
+ }
+}
+
+#endif
+
+// ============================= TestFuzzyPoliciesIncDirLimASCII ==============
+
+// Same as TestFuzzyPoliciesIncDir() but using limited ASCII,
+// which represents more likely input.
+
+#if RUN_OFFLINE_TESTS
+
+TEST(CSPParser, FuzzyPoliciesIncDirLimASCII)
+{
+ char input[] = "1234567890" \
+ "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWZYZ" \
+ "!@#^&*()-+_=";
+
+ // init srand with 0 so we get same results
+ srand(0);
+
+ PolicyTest testPol[1];
+ memset(&testPol[0].policy, '\0', kMaxPolicyLength);
+
+ char defaultSrc[] = "default-src ";
+ int defaultSrcLen = sizeof(defaultSrc) - 1;
+ // copy default-src into the policy array
+ memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
+
+ for (uint32_t index = 0; index < kFuzzyRuns; index++) {
+ // randomly select the length of the next policy
+ uint32_t polLength = rand() % (kMaxPolicyLength - defaultSrcLen);
+ // reset memory of the policy string, but leave default-src.
+ memset((&(testPol[0].policy) + (defaultSrcLen * sizeof(char))),
+ '\0', (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
+
+ // do not start at index 0 so we do not overwrite 'default-src'
+ for (uint32_t i = defaultSrcLen; i < polLength; i++) {
+ // fill the policy array with chars from the pre-defined input
+ uint32_t inputIndex = rand() % sizeof(input);
+ testPol[0].policy[i] = input[inputIndex];
+ }
+ ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+ kFuzzyExpectedPolicyCount)));
+ }
+}
+#endif
+
diff --git a/dom/security/test/gtest/moz.build b/dom/security/test/gtest/moz.build
new file mode 100644
index 000000000..e927e7bfa
--- /dev/null
+++ b/dom/security/test/gtest/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'TestCSPParser.cpp',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
diff --git a/dom/security/test/hsts/browser.ini b/dom/security/test/hsts/browser.ini
new file mode 100644
index 000000000..ae75031df
--- /dev/null
+++ b/dom/security/test/hsts/browser.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+skip-if = debug # bug 1311599, bug 1311239, etc
+support-files =
+ head.js
+ file_priming-top.html
+ file_testserver.sjs
+ file_1x1.png
+ file_priming.js
+ file_stylesheet.css
+
+[browser_hsts-priming_allow_active.js]
+[browser_hsts-priming_block_active.js]
+[browser_hsts-priming_hsts_after_mixed.js]
+[browser_hsts-priming_allow_display.js]
+[browser_hsts-priming_block_display.js]
+[browser_hsts-priming_block_active_css.js]
+[browser_hsts-priming_block_active_with_redir_same.js]
+[browser_hsts-priming_no-duplicates.js]
+[browser_hsts-priming_cache-timeout.js]
diff --git a/dom/security/test/hsts/browser_hsts-priming_allow_active.js b/dom/security/test/hsts/browser_hsts-priming_allow_active.js
new file mode 100644
index 000000000..a932b31b3
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_allow_active.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when active
+ * content is allowed.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "allow_active";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_allow_display.js b/dom/security/test/hsts/browser_hsts-priming_allow_display.js
new file mode 100644
index 000000000..06546ca65
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_allow_display.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when display
+ * content is allowed.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "allow_display";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active.js b/dom/security/test/hsts/browser_hsts-priming_block_active.js
new file mode 100644
index 000000000..a5478b185
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_block_active.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when active
+ * content is blocked.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_active";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active_css.js b/dom/security/test/hsts/browser_hsts-priming_block_active_css.js
new file mode 100644
index 000000000..340d11483
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_block_active_css.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when active
+ * content is blocked for css.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_active_css";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js b/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js
new file mode 100644
index 000000000..130a3d5ec
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when active
+ * content is blocked and redirect to the same host should still upgrade.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_active_with_redir_same";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_block_display.js b/dom/security/test/hsts/browser_hsts-priming_block_display.js
new file mode 100644
index 000000000..4eca62718
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_block_display.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when display
+ * content is blocked.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_display";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js b/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js
new file mode 100644
index 000000000..5416a71d2
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js
@@ -0,0 +1,36 @@
+/*
+ * Description of the test:
+ * Test that the network.hsts_priming.cache_timeout preferene causes the cache
+ * to timeout
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Observer.add_observers(Services);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_display";
+
+ SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_cache_timeout", 1]]);
+
+ yield execute_test("no-ssl", test_settings[which].mimetype);
+
+ let pre_promise = performance.now();
+
+ while ((performance.now() - pre_promise) < 2000) {
+ yield new Promise(function (resolve) {
+ setTimeout(resolve, 2000);
+ });
+ }
+
+ // clear the fact that we saw a priming request
+ test_settings[which].priming = {};
+
+ yield execute_test("no-ssl", test_settings[which].mimetype);
+ is(test_settings[which].priming["no-ssl"], true,
+ "Correctly send a priming request after expiration.");
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js b/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js
new file mode 100644
index 000000000..89ea6fbeb
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js
@@ -0,0 +1,24 @@
+/*
+ * Description of the test:
+ * Check that HSTS priming occurs correctly with mixed content when the
+ * mixed-content blocks before HSTS.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Services.obs.addObserver(Observer, "console-api-log-event", false);
+ Services.obs.addObserver(Observer, "http-on-examine-response", false);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "hsts_after_mixed";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js b/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js
new file mode 100644
index 000000000..3846fe4f0
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js
@@ -0,0 +1,30 @@
+/*
+ * Description of the test:
+ * Only one request should be sent per host, even if we run the test more
+ * than once.
+ */
+'use strict';
+
+//jscs:disable
+add_task(function*() {
+ //jscs:enable
+ Observer.add_observers(Services);
+ registerCleanupFunction(do_cleanup);
+
+ let which = "block_display";
+
+ SetupPrefTestEnvironment(which);
+
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ test_settings[which].priming = {};
+
+ // run the tests twice to validate the cache is being used
+ for (let server of Object.keys(test_servers)) {
+ yield execute_test(server, test_settings[which].mimetype);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/security/test/hsts/file_1x1.png b/dom/security/test/hsts/file_1x1.png
new file mode 100644
index 000000000..1ba31ba1a
--- /dev/null
+++ b/dom/security/test/hsts/file_1x1.png
Binary files differ
diff --git a/dom/security/test/hsts/file_priming-top.html b/dom/security/test/hsts/file_priming-top.html
new file mode 100644
index 000000000..b1d1bfa40
--- /dev/null
+++ b/dom/security/test/hsts/file_priming-top.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1246540</title>
+ <meta http-equiv='content-type' content="text/html;charset=utf-8" />
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="visibility: hidden">
+ </div>
+
+<script type="text/javascript">
+/*
+ * Description of the test:
+ * Attempt to load an insecure resource. If the resource responds to HSTS
+ * priming with an STS header, the load should continue securely.
+ * If it does not, the load should continue be blocked or continue insecurely.
+ */
+
+function parse_query_string() {
+ var q = {};
+ document.location.search.substr(1).
+ split('&').forEach(function (item, idx, ar) {
+ let [k, v] = item.split('=');
+ q[k] = unescape(v);
+ });
+ return q;
+}
+
+var args = parse_query_string();
+
+var subresources = {
+ css: { mimetype: 'text/css', file: 'file_stylesheet.css' },
+ img: { mimetype: 'image/png', file: 'file_1x1.png' },
+ script: { mimetype: 'text/javascript', file: 'file_priming.js' },
+};
+
+function handler(ev) {
+ console.log("HSTS_PRIMING: Blocked "+args.id);
+}
+
+function loadCss(src) {
+ let head = document.getElementsByTagName("head")[0];
+ let link = document.createElement("link");
+ link.setAttribute("rel", "stylesheet");
+ link.setAttribute("type", subresources[args.type].mimetype);
+ link.setAttribute("href", src);
+ head.appendChild(link);
+}
+
+function loadResource(src) {
+ let content = document.getElementById("content");
+ let testElem = document.createElement(args.type);
+ testElem.setAttribute("id", args.id);
+ testElem.setAttribute("charset", "UTF-8");
+ testElem.onerror = handler;
+ content.appendChild(testElem);
+ testElem.src = src;
+}
+
+function loadTest() {
+ let subresource = subresources[args.type];
+
+ let src = "http://"
+ + args.host
+ + "/browser/dom/security/test/hsts/file_testserver.sjs"
+ + "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file)
+ + "&primer=" + escape(args.id)
+ + "&mimetype=" + escape(subresource.mimetype)
+ ;
+ if (args.type == 'css') {
+ loadCss(src);
+ return;
+ }
+
+ loadResource(src);
+}
+
+// start running the tests
+loadTest();
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/hsts/file_priming.js b/dom/security/test/hsts/file_priming.js
new file mode 100644
index 000000000..023022da6
--- /dev/null
+++ b/dom/security/test/hsts/file_priming.js
@@ -0,0 +1,4 @@
+function completed() {
+ return;
+}
+completed();
diff --git a/dom/security/test/hsts/file_stylesheet.css b/dom/security/test/hsts/file_stylesheet.css
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/dom/security/test/hsts/file_stylesheet.css
diff --git a/dom/security/test/hsts/file_testserver.sjs b/dom/security/test/hsts/file_testserver.sjs
new file mode 100644
index 000000000..d5cd6b17a
--- /dev/null
+++ b/dom/security/test/hsts/file_testserver.sjs
@@ -0,0 +1,66 @@
+// SJS file for HSTS mochitests
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function loadFromFile(path) {
+ // Load the HTML to return in the response from file.
+ // Since it's relative to the cwd of the test runner, we start there and
+ // append to get to the actual path of the file.
+ var testFile =
+ Components.classes["@mozilla.org/file/directory_service;1"].
+ getService(Components.interfaces.nsIProperties).
+ get("CurWorkD", Components.interfaces.nsILocalFile);
+ var dirs = path.split("/");
+ for (var i = 0; i < dirs.length; i++) {
+ testFile.append(dirs[i]);
+ }
+ var testFileStream =
+ Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ testFileStream.init(testFile, -1, 0, 0);
+ var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available());
+ return test;
+}
+
+function handleRequest(request, response)
+{
+ const query = new URLSearchParams(request.queryString);
+
+ redir = query.get('redir');
+ if (redir == 'same') {
+ query.delete("redir");
+ response.setStatus(302);
+ let newURI = request.uri;
+ newURI.queryString = query.serialize();
+ response.setHeader("Location", newURI.spec)
+ }
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ // if we have a priming header, check for required behavior
+ // and set header appropriately
+ if (request.hasHeader('Upgrade-Insecure-Requests')) {
+ var expected = query.get('primer');
+ if (expected == 'prime-hsts') {
+ // set it for 5 minutes
+ response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false);
+ } else if (expected == 'reject-upgrade') {
+ response.setHeader("Strict-Transport-Security", "max-age=0", false);
+ }
+ response.write('');
+ return;
+ }
+
+ var file = query.get('file');
+ if (file) {
+ var mimetype = unescape(query.get('mimetype'));
+ response.setHeader("Content-Type", mimetype, false);
+ response.write(loadFromFile(unescape(file)));
+ return;
+ }
+
+ response.setHeader("Content-Type", "application/json", false);
+ response.write('{}');
+}
diff --git a/dom/security/test/hsts/head.js b/dom/security/test/hsts/head.js
new file mode 100644
index 000000000..362b36444
--- /dev/null
+++ b/dom/security/test/hsts/head.js
@@ -0,0 +1,308 @@
+/*
+ * Description of the tests:
+ * Check that HSTS priming occurs correctly with mixed content
+ *
+ * This test uses three hostnames, each of which treats an HSTS priming
+ * request differently.
+ * * no-ssl never returns an ssl response
+ * * reject-upgrade returns an ssl response, but with no STS header
+ * * prime-hsts returns an ssl response with the appropriate STS header
+ *
+ * For each server, test that it response appropriately when the we allow
+ * or block active or display content, as well as when we send an hsts priming
+ * request, but do not change the order of mixed-content and HSTS.
+ *
+ * Test use http-on-examine-response, so must be run in browser context.
+ */
+'use strict';
+
+var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html";
+
+var test_servers = {
+ // a test server that does not support TLS
+ 'no-ssl': {
+ host: 'example.co.jp',
+ response: false,
+ id: 'no-ssl',
+ },
+ // a test server which does not support STS upgrade
+ 'reject-upgrade': {
+ host: 'example.org',
+ response: true,
+ id: 'reject-upgrade',
+ },
+ // a test server when sends an STS header when priming
+ 'prime-hsts': {
+ host: 'test1.example.com',
+ response: true,
+ id: 'prime-hsts'
+ },
+};
+
+var test_settings = {
+ // mixed active content is allowed, priming will upgrade
+ allow_active: {
+ block_active: false,
+ block_display: false,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'script',
+ result: {
+ 'no-ssl': 'insecure',
+ 'reject-upgrade': 'insecure',
+ 'prime-hsts': 'secure',
+ },
+ },
+ // mixed active content is blocked, priming will upgrade
+ block_active: {
+ block_active: true,
+ block_display: false,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'script',
+ result: {
+ 'no-ssl': 'blocked',
+ 'reject-upgrade': 'blocked',
+ 'prime-hsts': 'secure',
+ },
+ },
+ // keep the original order of mixed-content and HSTS, but send
+ // priming requests
+ hsts_after_mixed: {
+ block_active: true,
+ block_display: false,
+ use_hsts: false,
+ send_hsts_priming: true,
+ type: 'script',
+ result: {
+ 'no-ssl': 'blocked',
+ 'reject-upgrade': 'blocked',
+ 'prime-hsts': 'blocked',
+ },
+ },
+ // mixed display content is allowed, priming will upgrade
+ allow_display: {
+ block_active: true,
+ block_display: false,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'img',
+ result: {
+ 'no-ssl': 'insecure',
+ 'reject-upgrade': 'insecure',
+ 'prime-hsts': 'secure',
+ },
+ },
+ // mixed display content is blocked, priming will upgrade
+ block_display: {
+ block_active: true,
+ block_display: true,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'img',
+ result: {
+ 'no-ssl': 'blocked',
+ 'reject-upgrade': 'blocked',
+ 'prime-hsts': 'secure',
+ },
+ },
+ // mixed active content is blocked, priming will upgrade (css)
+ block_active_css: {
+ block_active: true,
+ block_display: false,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'css',
+ result: {
+ 'no-ssl': 'blocked',
+ 'reject-upgrade': 'blocked',
+ 'prime-hsts': 'secure',
+ },
+ },
+ // mixed active content is blocked, priming will upgrade
+ // redirect to the same host
+ block_active_with_redir_same: {
+ block_active: true,
+ block_display: false,
+ use_hsts: true,
+ send_hsts_priming: true,
+ type: 'script',
+ redir: 'same',
+ result: {
+ 'no-ssl': 'blocked',
+ 'reject-upgrade': 'blocked',
+ 'prime-hsts': 'secure',
+ },
+ },
+}
+// track which test we are on
+var which_test = "";
+
+const Observer = {
+ observe: function (subject, topic, data) {
+ switch (topic) {
+ case 'console-api-log-event':
+ return Observer.console_api_log_event(subject, topic, data);
+ case 'http-on-examine-response':
+ return Observer.http_on_examine_response(subject, topic, data);
+ case 'http-on-modify-request':
+ return Observer.http_on_modify_request(subject, topic, data);
+ }
+ throw "Can't handle topic "+topic;
+ },
+ add_observers: function (services) {
+ services.obs.addObserver(Observer, "console-api-log-event", false);
+ services.obs.addObserver(Observer, "http-on-examine-response", false);
+ services.obs.addObserver(Observer, "http-on-modify-request", false);
+ },
+ // When a load is blocked which results in an error event within a page, the
+ // test logs to the console.
+ console_api_log_event: function (subject, topic, data) {
+ var message = subject.wrappedJSObject.arguments[0];
+ // when we are blocked, this will match the message we sent to the console,
+ // ignore everything else.
+ var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/);
+ if (!re.test(message)) {
+ return;
+ }
+
+ let id = message.replace(re, '$1');
+ let curTest =test_servers[id];
+
+ if (!curTest) {
+ ok(false, "HSTS priming got a console message blocked, "+
+ "but doesn't match expectations "+id+" (msg="+message);
+ return;
+ }
+
+ is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+
+ which_test+":"+curTest.id+" expected "+
+ test_settings[which_test].result[curTest.id]+", got blocked");
+ test_settings[which_test].finished[curTest.id] = "blocked";
+ },
+ get_current_test: function(uri) {
+ for (let item in test_servers) {
+ let re = RegExp('https?://'+test_servers[item].host);
+ if (re.test(uri)) {
+ return test_servers[item];
+ }
+ }
+ return null;
+ },
+ http_on_modify_request: function (subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (channel.requestMethod != 'HEAD') {
+ return;
+ }
+
+ let curTest = this.get_current_test(channel.URI.asciiSpec);
+
+ if (!curTest) {
+ return;
+ }
+
+ ok(!(curTest.id in test_settings[which_test].priming), "Already saw a priming request for " + curTest.id);
+ test_settings[which_test].priming[curTest.id] = true;
+ },
+ // When we see a response come back, peek at the response and test it is secure
+ // or insecure as needed. Addtionally, watch the response for priming requests.
+ http_on_examine_response: function (subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ let curTest = this.get_current_test(channel.URI.asciiSpec);
+
+ if (!curTest) {
+ return;
+ }
+
+ let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure";
+
+ // This is a priming request, go ahead and validate we were supposed to see
+ // a response from the server
+ if (channel.requestMethod == 'HEAD') {
+ is(true, curTest.response, "HSTS priming response found " + curTest.id);
+ return;
+ }
+
+ // This is the response to our query, make sure it matches
+ is(result, test_settings[which_test].result[curTest.id],
+ "HSTS priming result " + which_test + ":" + curTest.id);
+ test_settings[which_test].finished[curTest.id] = result;
+ },
+};
+
+// opens `uri' in a new tab and focuses it.
+// returns the newly opened tab
+function openTab(uri) {
+ let tab = gBrowser.addTab(uri);
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerDocument.defaultView.focus();
+
+ return tab;
+}
+
+function clear_sts_data() {
+ for (let test in test_servers) {
+ SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host);
+ }
+}
+
+function do_cleanup() {
+ clear_sts_data();
+
+ Services.obs.removeObserver(Observer, "console-api-log-event");
+ Services.obs.removeObserver(Observer, "http-on-examine-response");
+}
+
+function SetupPrefTestEnvironment(which, additional_prefs) {
+ which_test = which;
+ clear_sts_data();
+
+ var settings = test_settings[which];
+ // priming counts how many priming requests we saw
+ settings.priming = {};
+ // priming counts how many tests were finished
+ settings.finished= {};
+
+ var prefs = [["security.mixed_content.block_active_content",
+ settings.block_active],
+ ["security.mixed_content.block_display_content",
+ settings.block_display],
+ ["security.mixed_content.use_hsts",
+ settings.use_hsts],
+ ["security.mixed_content.send_hsts_priming",
+ settings.send_hsts_priming]];
+
+ if (additional_prefs) {
+ for (let idx in additional_prefs) {
+ prefs.push(additional_prefs[idx]);
+ }
+ }
+
+ console.log("prefs=%s", prefs);
+
+ SpecialPowers.pushPrefEnv({'set': prefs});
+}
+
+// make the top-level test uri
+function build_test_uri(base_uri, host, test_id, type) {
+ return base_uri +
+ "?host=" + escape(host) +
+ "&id=" + escape(test_id) +
+ "&type=" + escape(type);
+}
+
+// open a new tab, load the test, and wait for it to finish
+function execute_test(test, mimetype) {
+ var src = build_test_uri(TOP_URI, test_servers[test].host,
+ test, test_settings[which_test].type);
+
+ let tab = openTab(src);
+ test_servers[test]['tab'] = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+
+ yield BrowserTestUtils.removeTab(tab);
+}
diff --git a/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html b/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html
new file mode 100644
index 000000000..f1459d366
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Mailto Protocol Compose Page
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head> <meta charset="utf-8">
+</head>
+<body>
+Hello
+<script>window.close();</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation.html b/dom/security/test/mixedcontentblocker/file_frameNavigation.html
new file mode 100644
index 000000000..fd9ea2317
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker related to navigating children, grandchildren, etc
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<div id="testContent"></div>
+
+<script>
+ var baseUrlHttps = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html";
+
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1: Navigate secure iframe to insecure iframe on an insecure page
+ var iframe_test1 = document.createElement("iframe");
+ var counter_test1 = 0;
+ iframe_test1.src = baseUrlHttps + "?insecurePage_navigate_child";
+ iframe_test1.setAttribute("id", "test1");
+ iframe_test1.onerror = function() {
+ parent.postMessage({"test": "insecurePage_navigate_child", "msg": "got an onerror alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test1);
+
+ function navigationStatus(iframe_test1)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc = document.getElementById("test1").contentDocument.location;
+ } catch(e) {
+ if (e.name === "SecurityError") {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter_test1++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response") {
+ return;
+ }
+ else {
+ if(counter_test1 < MAX_COUNT) {
+ counter_test1++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.postMessage({"test": "insecurePage_navigate_child", "msg": "navigating to insecure iframe blocked on insecure page"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+
+ // Test 2: Navigate secure grandchild iframe to insecure grandchild iframe on a page that has no secure parents
+ var iframe_test2 = document.createElement("iframe");
+ iframe_test2.src = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html"
+ iframe_test2.onerror = function() {
+ parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "got an on error alert when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test2);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html
new file mode 100644
index 000000000..5cfd95984
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Opening link with _blank target in an https iframe.
+https://bugzilla.mozilla.org/show_bug.cgi?id=841850
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?blankTarget" id="blankTarget" target="_blank">Go to http site</a>
+
+<script>
+ var blankTarget = document.getElementById("blankTarget");
+ blankTarget.click();
+
+ var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"].
+ getService(SpecialPowers.Components.interfaces.nsIObserverService);
+ var observer = {
+ observe: function(subject, topic, data) {
+ if(topic == "content-document-global-created" && data =="http://example.com") {
+ parent.parent.postMessage({"test": "blankTarget", "msg": "opened an http link with target=_blank from a secure page"}, "http://mochi.test:8888");
+ os.removeObserver(observer, "content-document-global-created");
+ }
+ }
+ }
+ os.addObserver(observer, "content-document-global-created", false);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html
new file mode 100644
index 000000000..1034991da
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_grandchild.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Navigating Grandchild frames when a secure parent doesn't exist
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<iframe src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild" id="child"></iframe>
+
+<script>
+ // For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+ var counter = 0;
+
+ var child = document.getElementById("child");
+ function navigationStatus(child)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc;
+ if (child.contentDocument) {
+ loc = child.contentDocument.location;
+ }
+ } catch(e) {
+ if (e.message && e.message.indexOf("Permission denied to access property") == -1) {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild_response") {
+ return;
+ }
+ else {
+ if(counter < MAX_COUNT) {
+ counter++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "navigating to insecure grandchild iframe blocked on insecure page"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html
new file mode 100644
index 000000000..62b24f37d
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div id="content"></div>
+<script>
+ // get the case from the query string
+ var type = location.search.substring(1);
+
+ switch (type) {
+ case "insecurePage_navigate_child":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response" id="link">Testing\<\/a>';
+ document.getElementById("link").click();
+ break;
+
+ case "insecurePage_navigate_child_response":
+ parent.parent.postMessage({"test": "insecurePage_navigate_child", "msg": "navigated to insecure iframe on insecure page"}, "http://mochi.test:8888");
+ document.getElementById("content").innerHTML = "Navigated from secure to insecure frame on an insecure page";
+ break;
+
+ case "insecurePage_navigate_grandchild":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_grandchild_response" id="link">Testing\<\/a>';
+ // If we don't reflow before clicking the link, the test will fail intermittently. The reason is still unknown. We'll track this issue in bug 1259715.
+ requestAnimationFrame(function() {
+ setTimeout(function() {
+ document.getElementById("link").click();
+ }, 0);
+ });
+ break;
+
+ case "insecurePage_navigate_grandchild_response":
+ parent.parent.parent.postMessage({"test": "insecurePage_navigate_grandchild", "msg": "navigated to insecure grandchild iframe on insecure page"}, "http://mochi.test:8888");
+ document.getElementById("content").innerHTML = "Navigated from secure to insecure grandchild frame on an insecure page";
+ break;
+
+ case "securePage_navigate_child":
+ document.getElementById("content").innerHTML =
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_child_response" id="link">Testing\<\/a>';
+ document.getElementById("link").click();
+ break;
+
+ case "securePage_navigate_child_response":
+ document.getElementById("content").innerHTML = "<p>Navigated from secure to insecure frame on a secure page</p>";
+ parent.parent.postMessage({"test": "securePage_navigate_child", "msg": "navigated to insecure iframe on secure page"}, "http://mochi.test:8888");
+ break;
+
+ case "securePage_navigate_grandchild":
+ document.getElementById("content").innerHTML=
+ '<a href="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild_response" id="link">Testing\<\/a>';
+ document.getElementById("link").click();
+ break;
+
+ case "securePage_navigate_grandchild_response":
+ dump("\nNavigated to grandchild iframe from secure location to insecure location. About to post message to the top page.\n");
+ parent.parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigated to insecure grandchild iframe on secure page"}, "http://mochi.test:8888");
+ dump("\npostMessage to parent attempted.\n");
+ document.getElementById("content").innerHTML = "<p>Navigated from secure to insecure grandchild frame on a secure page</p>";
+ break;
+
+ case "blankTarget":
+ window.close();
+ break;
+
+ default:
+ document.getElementById("content").innerHTML = "Hello";
+ break;
+ }
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html
new file mode 100644
index 000000000..ea8462c39
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker related to navigating children, grandchildren, etc
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+<div id="testContent"></div>
+
+<script>
+ var baseUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html";
+
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1: Navigate secure iframe to insecure iframe on a secure page
+ var iframe_test1 = document.createElement("iframe");
+ var counter_test1 = 0;
+ iframe_test1.setAttribute("id", "test1");
+ iframe_test1.src = baseUrl + "?securePage_navigate_child";
+ iframe_test1.onerror = function() {
+ parent.postMessage({"test": "securePage_navigate_child", "msg": "got an onerror event when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test1);
+
+ function navigationStatus(iframe_test1)
+ {
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ var loc = document.getElementById("test1").contentDocument.location;
+ } catch(e) {
+ if (e.name === "SecurityError") {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter_test1++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?insecurePage_navigate_child_response") {
+ return;
+ } else {
+ if(counter_test1 < MAX_COUNT) {
+ counter_test1++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ parent.postMessage({"test": "securePage_navigate_child", "msg": "navigating to insecure iframe blocked on secure page"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, iframe_test1);
+
+ // Test 2: Open an http page in a new tab from a link click with target=_blank.
+ var iframe_test3 = document.createElement("iframe");
+ iframe_test3.src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_blankTarget.html";
+ iframe_test3.onerror = function() {
+ parent.postMessage({"test": "blankTarget", "msg": "got an onerror event when loading or navigating testing iframe"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe_test3);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html
new file mode 100644
index 000000000..f7f3c4086
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_frameNavigation_secure_grandchild.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Navigating Grandchild Frames when a secure parent exists
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Mixed Content Frame Navigation</title>
+</head>
+<body>
+
+<iframe src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild" id="child"></iframe>
+<script>
+ // For tests that require setTimeout, set the maximum polling time to 50 x 100ms = 5 seconds.
+ var MAX_COUNT = 50;
+ var TIMEOUT_INTERVAL = 100;
+ var counter = 0;
+
+ var child = document.getElementById("child");
+ function navigationStatus(child)
+ {
+ var loc;
+ // When the page is navigating, it goes through about:blank and we will get a permission denied for loc.
+ // Catch that specific exception and return
+ try {
+ loc = document.getElementById("child").contentDocument.location;
+ } catch(e) {
+ if (e.message && e.message.indexOf("Permission denied to access property") == -1) {
+ // We received an exception we didn't expect.
+ throw e;
+ }
+ counter++;
+ return;
+ }
+ if (loc == "http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_innermost.html?securePage_navigate_grandchild_response") {
+ return;
+ }
+ else {
+ if(counter < MAX_COUNT) {
+ counter++;
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+ }
+ else {
+ // After we have called setTimeout the maximum number of times, assume navigating the iframe is blocked
+ dump("\nThe current location of the grandchild iframe is: "+loc+".\n");
+ dump("\nWe have past the maximum timeout. Navigating a grandchild iframe from an https location to an http location on a secure page failed. We are about to post message to the top level page\n");
+ parent.parent.postMessage({"test": "securePage_navigate_grandchild", "msg": "navigating to insecure grandchild iframe blocked on secure page"}, "http://mochi.test:8888");
+ dump("\nAttempted postMessage\n");
+ }
+ }
+ }
+
+ setTimeout(navigationStatus, TIMEOUT_INTERVAL, child);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main.html b/dom/security/test/mixedcontentblocker/file_main.html
new file mode 100644
index 000000000..ade5eefdb
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main.html
@@ -0,0 +1,261 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=62178
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="testContent"></div>
+
+<!-- types the Mixed Content Blocker can block
+ /*
+ switch (aContentType) {
+ case nsIContentPolicy::TYPE_OBJECT:
+ case nsIContentPolicy::TYPE_SCRIPT:
+ case nsIContentPolicy::TYPE_STYLESHEET:
+ case nsIContentPolicy::TYPE_SUBDOCUMENT:
+ case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
+
+ case nsIContentPolicy::TYPE_FONT: - NO TEST:
+ Load events for external fonts are not detectable by javascript.
+ case nsIContentPolicy::TYPE_WEBSOCKET: - NO TEST:
+ websocket connections over https require an encrypted websocket protocol (wss:)
+
+ case nsIContentPolicy::TYPE_IMAGE:
+ case nsIContentPolicy::TYPE_IMAGESET:
+ case nsIContentPolicy::TYPE_MEDIA:
+ case nsIContentPolicy::TYPE_PING:
+ our ping implementation is off by default and does not comply with the current spec (bug 786347)
+ case nsIContentPolicy::TYPE_BEACON:
+
+ }
+ */
+-->
+
+<script>
+ var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs";
+
+ //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
+ var MAX_COUNT = 100;
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ /* Part 1: Mixed Script tests */
+
+ // Test 1a: insecure object
+ var object = document.createElement("object");
+ object.data = baseUrl + "?type=object";
+ object.type = "application/x-test";
+ object.width = "200";
+ object.height = "200";
+
+ testContent.appendChild(object);
+
+ var objectCount = 0;
+
+ function objectStatus(object) {
+ // Expose our privileged bits on the object
+ object = SpecialPowers.wrap(object);
+
+ if (object.displayedType != SpecialPowers.Ci.nsIObjectLoadingContent.TYPE_NULL) {
+ //object loaded
+ parent.postMessage({"test": "object", "msg": "insecure object loaded"}, "http://mochi.test:8888");
+ }
+ else {
+ if(objectCount < MAX_COUNT) {
+ objectCount++;
+ setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume object is blocked
+ parent.postMessage({"test": "object", "msg": "insecure object blocked"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status
+ setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
+
+ // Test 1b: insecure script
+ var script = document.createElement("script");
+ var scriptLoad = false;
+ var scriptCount = 0;
+ script.src = baseUrl + "?type=script";
+ script.onload = function() {
+ parent.postMessage({"test": "script", "msg": "insecure script loaded"}, "http://mochi.test:8888");
+ scriptLoad = true;
+ }
+ testContent.appendChild(script);
+
+ function scriptStatus(script)
+ {
+ if(scriptLoad) {
+ return;
+ }
+ else {
+ if(scriptCount < MAX_COUNT) {
+ scriptCount++;
+ setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume script is blocked
+ parent.postMessage({"test": "script", "msg": "insecure script blocked"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ // scripts blocked by Content Policy's do not have onerror events (see bug 789856). Hence we need a setTimeout to check the script's status
+ setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
+
+
+ // Test 1c: insecure stylesheet
+ var cssStyleSheet = document.createElement("link");
+ cssStyleSheet.rel = "stylesheet";
+ cssStyleSheet.href = baseUrl + "?type=stylesheet";
+ cssStyleSheet.type = "text/css";
+ testContent.appendChild(cssStyleSheet);
+
+ var styleCount = 0;
+
+ function styleStatus(cssStyleSheet) {
+ if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) {
+ parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet loaded"}, "http://mochi.test:8888");
+ }
+ else {
+ if(styleCount < MAX_COUNT) {
+ styleCount++;
+ setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+ }
+ else {
+ //After we have called setTimeout the maximum number of times, assume stylesheet is blocked
+ parent.postMessage({"test": "stylesheet", "msg": "insecure stylesheet blocked"}, "http://mochi.test:8888");
+ }
+ }
+ }
+
+ // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status
+ window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
+
+ // Test 1d: insecure iframe
+ var iframe = document.createElement("iframe");
+ iframe.src = baseUrl + "?type=iframe";
+ iframe.onload = function() {
+ parent.postMessage({"test": "iframe", "msg": "insecure iframe loaded"}, "http://mochi.test:8888");
+ }
+ iframe.onerror = function() {
+ parent.postMessage({"test": "iframe", "msg": "insecure iframe blocked"}, "http://mochi.test:8888");
+ };
+ testContent.appendChild(iframe);
+
+
+ // Test 1e: insecure xhr
+ var xhr = new XMLHttpRequest;
+ try {
+ xhr.open("GET", baseUrl + "?type=xhr", true);
+ xhr.send();
+ xhr.onloadend = function (oEvent) {
+ if (xhr.status == 200) {
+ parent.postMessage({"test": "xhr", "msg": "insecure xhr loaded"}, "http://mochi.test:8888");
+ }
+ else {
+ parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
+ }
+ }
+ } catch(ex) {
+ parent.postMessage({"test": "xhr", "msg": "insecure xhr blocked"}, "http://mochi.test:8888");
+ }
+
+ /* Part 2: Mixed Display tests */
+
+ // Shorthand for all image test variants
+ function imgHandlers(img, test) {
+ img.onload = function () {
+ parent.postMessage({"test": test, "msg": "insecure image loaded"}, "http://mochi.test:8888");
+ }
+ img.onerror = function() {
+ parent.postMessage({"test": test, "msg": "insecure image blocked"}, "http://mochi.test:8888");
+ }
+ }
+
+ // Test 2a: insecure image
+ var img = document.createElement("img");
+ img.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ imgHandlers(img, "image");
+ // We don't need to append the image to the document. Doing so causes the image test to run twice.
+
+ // Test 2b: insecure media
+ var media = document.createElement("video");
+ media.src = "http://mochi.test:8888/tests/dom/media/test/320x240.ogv?" + Math.floor((Math.random()*1000)+1);
+ media.width = "320";
+ media.height = "200";
+ media.type = "video/ogg";
+ media.onloadeddata = function() {
+ parent.postMessage({"test": "media", "msg": "insecure media loaded"}, "http://mochi.test:8888");
+ }
+ media.onerror = function() {
+ parent.postMessage({"test": "media", "msg": "insecure media blocked"}, "http://mochi.test:8888");
+ }
+ // We don't need to append the video to the document. Doing so causes the image test to run twice.
+
+ /* Part 3: Mixed Active Tests for Image srcset */
+
+ // Test 3a: image with srcset
+ var imgA = document.createElement("img");
+ imgA.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ imgHandlers(imgA, "imageSrcset");
+
+ // Test 3b: image with srcset, using fallback from src, should still use imageset policy
+ var imgB = document.createElement("img");
+ imgB.srcset = " ";
+ imgB.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ imgHandlers(imgB, "imageSrcsetFallback");
+
+ // Test 3c: image in <picture>
+ var imgC = document.createElement("img");
+ var pictureC = document.createElement("picture");
+ var sourceC = document.createElement("source");
+ sourceC.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ pictureC.appendChild(sourceC);
+ pictureC.appendChild(imgC);
+ imgHandlers(imgC, "imagePicture");
+
+ // Test 3d: Loaded basic image switching to a <picture>, loading
+ // same source, should still redo the request with new
+ // policy.
+ var imgD = document.createElement("img");
+ imgD.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ imgD.onload = imgD.onerror = function() {
+ // Whether or not it loads, we want to now append it to a picture and observe
+ var pictureD = document.createElement("picture");
+ var sourceD = document.createElement("source");
+ sourceD.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ pictureD.appendChild(sourceD);
+ pictureD.appendChild(imgD);
+ imgHandlers(imgD, "imageJoinPicture");
+ }
+
+ // Test 3e: img load from <picture> source reverts to img.src as it
+ // is removed -- the new request should revert to mixed
+ // display policy
+ var imgE = document.createElement("img");
+ var pictureE = document.createElement("picture");
+ var sourceE = document.createElement("source");
+ sourceE.srcset = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ pictureE.appendChild(sourceE);
+ pictureE.appendChild(imgE);
+ imgE.src = "http://mochi.test:8888/tests/image/test/mochitest/blue.png";
+ imgE.onload = imgE.onerror = function() {
+ // Whether or not it loads, remove it from the picture and observe
+ pictureE.removeChild(imgE)
+ imgHandlers(imgE, "imageLeavePicture");
+ }
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main_bug803225.html b/dom/security/test/mixedcontentblocker/file_main_bug803225.html
new file mode 100644
index 000000000..e657be256
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main_bug803225.html
@@ -0,0 +1,182 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker - Allowed Protocols
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="testContent"></div>
+
+<!-- Test additional schemes the Mixed Content Blocker should not block
+ "about" protocol URIs that are URI_SAFE_FOR_UNTRUSTED_CONTENT (moz-safe-about; see nsAboutProtocolHandler::NewURI
+ "data",
+ "javascript",
+ "mailto",
+ "resource",
+ "moz-icon",
+ "wss"
+-->
+
+<script>
+
+ //For tests that require setTimeout, set the timeout interval
+ var TIMEOUT_INTERVAL = 100;
+
+ var testContent = document.getElementById("testContent");
+
+ // Test 1 & 2: about and javascript protcols within an iframe
+ var data = Array(2,2);
+ var protocols = [
+ ["about", ""], //When no source is specified, the frame gets a source of about:blank
+ ["javascript", "javascript:document.open();document.write='<h1>SUCCESS</h1>';document.close();"],
+ ];
+ for(var i=0; i < protocols.length; i++)
+ {
+ var generic_frame = document.createElement("iframe");
+ generic_frame.src = protocols[i][1];
+ generic_frame.name="generic_protocol";
+
+ generic_frame.onload = function(i) {
+ data = {"test": protocols[i][0], "msg": "resource with " + protocols[i][0] + " protocol loaded"};
+ parent.postMessage(data, "http://mochi.test:8888");
+ }.bind(generic_frame, i)
+
+ generic_frame.onerror = function(i) {
+ data = {"test": protocols[i][0], "msg": "resource with " + protocols[i][0] + " protocol did not load"};
+ parent.postMessage(data, "http://mochi.test:8888");
+ }.bind(generic_frame, i);
+
+ testContent.appendChild(generic_frame, i);
+ }
+
+ // Test 3: for resource within a script tag
+ // Note: the script we load throws an exception, but the script element's
+ // onload listener is called after we successfully fetch the script,
+ // independently of whether it throws an exception.
+ var resource_script=document.createElement("script");
+ resource_script.src = "resource://gre/modules/XPCOMUtils.jsm";
+ resource_script.name = "resource_protocol";
+ resource_script.onload = function() {
+ parent.postMessage({"test": "resource", "msg": "resource with resource protocol loaded"}, "http://mochi.test:8888");
+ }
+ resource_script.onerror = function() {
+ parent.postMessage({"test": "resource", "msg": "resource with resource protocol did not load"}, "http://mochi.test:8888");
+ }
+
+ testContent.appendChild(resource_script);
+
+ // Test 4: moz-icon within an img tag
+ var image=document.createElement("img");
+ image.src = "moz-icon://dummy.exe?size=16";
+ image.onload = function() {
+ parent.postMessage({"test": "mozicon", "msg": "resource with mozicon protocol loaded"}, "http://mochi.test:8888");
+ }
+ image.onerror = function() {
+ parent.postMessage({"test": "mozicon", "msg": "resource with mozicon protocol did not load"}, "http://mochi.test:8888");
+ }
+ // We don't need to append the image to the document. Doing so causes the image test to run twice.
+
+ // Test 5: about unsafe protocol within an iframe
+ var unsafe_about_frame = document.createElement("iframe");
+ unsafe_about_frame.src = "about:config";
+ unsafe_about_frame.name = "unsafe_about_protocol";
+ unsafe_about_frame.onload = function() {
+ parent.postMessage({"test": "unsafe_about", "msg": "resource with unsafe about protocol loaded"}, "http://mochi.test:8888");
+ }
+ unsafe_about_frame.onerror = function() {
+ parent.postMessage({"test": "unsafe_about", "msg": "resource with unsafe about protocol did not load"}, "http://mochi.test:8888");
+ }
+ testContent.appendChild(unsafe_about_frame);
+
+ // Test 6: data protocol within a script tag
+ var x = 2;
+ var newscript = document.createElement("script");
+ newscript.src= "data:text/javascript,var x = 4;";
+ newscript.onload = function() {
+ parent.postMessage({"test": "data_protocol", "msg": "resource with data protocol loaded"}, "http://mochi.test:8888");
+ }
+ newscript.onerror = function() {
+ parent.postMessage({"test": "data_protocol", "msg": "resource with data protocol did not load"}, "http://mochi.test:8888");
+ }
+ testContent.appendChild(newscript);
+
+ // Test 7: mailto protocol
+ let mm = SpecialPowers.loadChromeScript(function launchHandler() {
+ var { classes: Cc, interfaces: Ci } = Components;
+ var ioService = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+
+ var webHandler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
+ createInstance(Ci.nsIWebHandlerApp);
+ webHandler.name = "Web Handler";
+ webHandler.uriTemplate = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_bug803225_test_mailto.html?s=%";
+
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ Services.ppmm.addMessageListener("Test:content-ready", function contentReadyListener() {
+ Services.ppmm.removeMessageListener("Test:content-ready", contentReadyListener);
+ sendAsyncMessage("Test:content-ready-forward");
+ Services.ppmm.removeDelayedProcessScript(pScript);
+ })
+
+ var pScript = "data:,new " + function () {
+ var os = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ var observer = {
+ observe: function(subject, topic, data) {
+ if (topic == "content-document-global-created" && data == "http://example.com") {
+ sendAsyncMessage("Test:content-ready");
+ os.removeObserver(observer, "content-document-global-created");
+ }
+ }
+ };
+ os.addObserver(observer, "content-document-global-created", false);
+ }
+
+ Services.ppmm.loadProcessScript(pScript, true);
+
+ var uri = ioService.newURI("mailto:foo@bar.com", null, null);
+ webHandler.launchWithURI(uri);
+ });
+
+ var mailto = false;
+
+ mm.addMessageListener("Test:content-ready-forward", function contentReadyListener() {
+ mm.removeMessageListener("Test:content-ready-forward", contentReadyListener);
+ mailto = true;
+ parent.postMessage({"test": "mailto", "msg": "resource with mailto protocol loaded"}, "http://mochi.test:8888");
+ });
+
+ function mailtoProtocolStatus() {
+ if(!mailto) {
+ //There is no onerror event associated with the WebHandler, and hence we need a setTimeout to check the status
+ setTimeout(mailtoProtocolStatus, TIMEOUT_INTERVAL);
+ }
+ }
+
+ mailtoProtocolStatus();
+
+ // Test 8: wss protocol
+ var wss;
+ wss = new WebSocket("wss://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket");
+
+ var status_wss = "started";
+ wss.onopen = function(e) {
+ status_wss = "opened";
+ wss.close();
+ }
+ wss.onclose = function(e) {
+ if(status_wss == "opened") {
+ parent.postMessage({"test": "wss", "msg": "resource with wss protocol loaded"}, "http://mochi.test:8888");
+ } else {
+ parent.postMessage({"test": "wss", "msg": "resource with wss protocol did not load"}, "http://mochi.test:8888");
+ }
+ }
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py b/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py
new file mode 100644
index 000000000..8c33c6b10
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_main_bug803225_websocket_wsh.py
@@ -0,0 +1,7 @@
+from mod_pywebsocket import msgutil
+
+def web_socket_do_extra_handshake(request):
+ pass
+
+def web_socket_transfer_data(request):
+ resp = ""
diff --git a/dom/security/test/mixedcontentblocker/file_server.sjs b/dom/security/test/mixedcontentblocker/file_server.sjs
new file mode 100644
index 000000000..58dffc565
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/file_server.sjs
@@ -0,0 +1,45 @@
+
+function handleRequest(request, response)
+{
+ // get the Content-Type to serve from the query string
+ var contentType = null;
+ request.queryString.split('&').forEach( function (val) {
+ var [name, value] = val.split('=');
+ if (name == "type") {
+ contentType = unescape(value);
+ }
+ });
+
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ switch (contentType) {
+ case "iframe":
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("frame content");
+ break;
+
+ case "script":
+ response.setHeader("Content-Type", "application/javascript", false);
+ break;
+
+ case "stylesheet":
+ response.setHeader("Content-Type", "text/css", false);
+ break;
+
+ case "object":
+ response.setHeader("Content-Type", "application/x-test", false);
+ break;
+
+ case "xhr":
+ response.setHeader("Content-Type", "text/xml", false);
+ response.setHeader("Access-Control-Allow-Origin", "https://example.com");
+ response.write('<?xml version="1.0" encoding="UTF-8" ?><test></test>');
+ break;
+
+ default:
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<html><body>Hello World</body></html>");
+ break;
+ }
+}
diff --git a/dom/security/test/mixedcontentblocker/mochitest.ini b/dom/security/test/mixedcontentblocker/mochitest.ini
new file mode 100644
index 000000000..94c17f9b4
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/mochitest.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+tags = mcb
+support-files =
+ file_bug803225_test_mailto.html
+ file_frameNavigation.html
+ file_frameNavigation_blankTarget.html
+ file_frameNavigation_grandchild.html
+ file_frameNavigation_innermost.html
+ file_frameNavigation_secure.html
+ file_frameNavigation_secure_grandchild.html
+ file_main.html
+ file_main_bug803225.html
+ file_main_bug803225_websocket_wsh.py
+ file_server.sjs
+ !/dom/media/test/320x240.ogv
+ !/image/test/mochitest/blue.png
+
+[test_main.html]
+skip-if = toolkit == 'android' #TIMED_OUT
+[test_bug803225.html]
+skip-if = toolkit == 'android' #TIMED_OUT
+[test_frameNavigation.html]
+skip-if = toolkit == 'android' #TIMED_OUT
diff --git a/dom/security/test/mixedcontentblocker/test_bug803225.html b/dom/security/test/mixedcontentblocker/test_bug803225.html
new file mode 100644
index 000000000..13c52762d
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_bug803225.html
@@ -0,0 +1,152 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Testing Whitelist of Resource Scheme for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=803225
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 803225</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ var counter = 0;
+ var settings = [ [true, true], [true, false], [false, true], [false, false] ];
+
+ var blockActive;
+ var blockDisplay;
+
+ //Cycle through 4 different preference settings.
+ function changePrefs(callback) {
+ let newPrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
+ ["security.mixed_content.block_active_content", settings[counter][1]]];
+
+ SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+ blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ counter++;
+ callback();
+ });
+ }
+
+ var testsToRun = {
+ /* https - Tests already run as part of bug 62178. */
+ about: false,
+ mozicon: false,
+ resource: false,
+ unsafe_about: false,
+ data_protocol: false,
+ javascript: false,
+ mailto: false,
+ wss: false,
+ };
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ function reloadFrame() {
+ document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225.html"></iframe>';
+ }
+
+ function checkTestsCompleted() {
+ for (var prop in testsToRun) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRun[prop])
+ return;
+ }
+ //if the testsToRun are all completed, change the pref and run the tests again until we have cycled through all the prefs.
+ if(counter < 4) {
+ for (var prop in testsToRun) {
+ testsToRun[prop] = false;
+ }
+ //call to change the preferences
+ changePrefs(function() {
+ log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+ reloadFrame();
+ });
+ }
+ else {
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTest = true;
+
+ function receiveMessage(event) {
+ if(firstTest) {
+ log("blockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+ firstTest = false;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ /* Mixed Script tests */
+ case "about":
+ ok(event.data.msg == "resource with about protocol loaded", "resource with about protocol did not load");
+ testsToRun["about"] = true;
+ break;
+
+ case "resource":
+ ok(event.data.msg == "resource with resource protocol loaded", "resource with resource protocol did not load");
+ testsToRun["resource"] = true;
+ break;
+
+ case "mozicon":
+ ok(event.data.msg == "resource with mozicon protocol loaded", "resource with mozicon protocol did not load");
+ testsToRun["mozicon"] = true;
+ break;
+
+ case "unsafe_about":
+ // This one should not load
+ ok(event.data.msg == "resource with unsafe about protocol did not load", "resource with unsafe about protocol loaded");
+ testsToRun["unsafe_about"] = true;
+ break;
+
+ case "data_protocol":
+ ok(event.data.msg == "resource with data protocol loaded", "resource with data protocol did not load");
+ testsToRun["data_protocol"] = true;
+ break;
+
+ case "javascript":
+ ok(event.data.msg == "resource with javascript protocol loaded", "resource with javascript protocol did not load");
+ testsToRun["javascript"] = true;
+ break;
+
+ case "wss":
+ ok(event.data.msg == "resource with wss protocol loaded", "resource with wss protocol did not load");
+ testsToRun["wss"] = true;
+ break;
+
+ case "mailto":
+ ok(event.data.msg == "resource with mailto protocol loaded", "resource with mailto protocol did not load");
+ testsToRun["mailto"] = true;
+ break;
+ }
+ checkTestsCompleted();
+ }
+
+ function startTest() {
+ //Set the first set of settings (true, true) and increment the counter.
+ changePrefs(function() {
+ // listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage, false);
+
+ reloadFrame();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+
+<body onload='startTest()'>
+ <div id="framediv"></div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_frameNavigation.html b/dom/security/test/mixedcontentblocker/test_frameNavigation.html
new file mode 100644
index 000000000..5b3ae50b0
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_frameNavigation.html
@@ -0,0 +1,127 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=840388
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 840388</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ var counter = 0;
+ var origBlockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+
+ SpecialPowers.setBoolPref("security.mixed_content.block_active_content", true);
+ var blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+
+
+ var testsToRunInsecure = {
+ insecurePage_navigate_child: false,
+ insecurePage_navigate_grandchild: false,
+ };
+
+ var testsToRunSecure = {
+ securePage_navigate_child: false,
+ blankTarget: false,
+ };
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ var secureTestsStarted = false;
+ function checkTestsCompleted() {
+ for (var prop in testsToRunInsecure) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRunInsecure[prop])
+ return;
+ }
+ // If we are here, all the insecure tests have run.
+ // If we haven't changed the iframe to run the secure tests, change it now.
+ if (!secureTestsStarted) {
+ document.getElementById('testing_frame').src = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation_secure.html";
+ secureTestsStarted = true;
+ }
+ for (var prop in testsToRunSecure) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRunSecure[prop])
+ return;
+ }
+ //if the secure and insecure testsToRun are all completed, change the block mixed active content pref and run the tests again.
+ if(counter < 1) {
+ for (var prop in testsToRunSecure) {
+ testsToRunSecure[prop] = false;
+ }
+ for (var prop in testsToRunInsecure) {
+ testsToRunInsecure[prop] = false;
+ }
+ //call to change the preferences
+ counter++;
+ SpecialPowers.setBoolPref("security.mixed_content.block_active_content", false);
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ log("blockActive set to "+blockActive+".");
+ secureTestsStarted = false;
+ document.getElementById('framediv').innerHTML = '<iframe src="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation.html" id="testing_frame"></iframe>';
+ }
+ else {
+ //set the prefs back to what they were set to originally
+ SpecialPowers.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTestDebugMessage = true;
+
+ // listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage, false);
+ function receiveMessage(event) {
+ if(firstTestDebugMessage) {
+ log("blockActive set to "+blockActive);
+ firstTestDebugMessage = false;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + ".");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ case "insecurePage_navigate_child":
+ is(event.data.msg, "navigated to insecure iframe on insecure page", "navigating to insecure iframe blocked on insecure page");
+ testsToRunInsecure["insecurePage_navigate_child"] = true;
+ break;
+
+ case "insecurePage_navigate_grandchild":
+ is(event.data.msg, "navigated to insecure grandchild iframe on insecure page", "navigating to insecure grandchild iframe blocked on insecure page");
+ testsToRunInsecure["insecurePage_navigate_grandchild"] = true;
+ break;
+
+ case "securePage_navigate_child":
+ ok(blockActive == (event.data.msg == "navigating to insecure iframe blocked on secure page"), "navigated to insecure iframe on secure page");
+ testsToRunSecure["securePage_navigate_child"] = true;
+ break;
+
+ case "blankTarget":
+ is(event.data.msg, "opened an http link with target=_blank from a secure page", "couldn't open an http link in a new window from a secure page");
+ testsToRunSecure["blankTarget"] = true;
+ break;
+
+ }
+ checkTestsCompleted();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+
+<body>
+ <div id="framediv">
+ <iframe src="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation.html" id="testing_frame"></iframe>
+ </div>
+
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/mixedcontentblocker/test_main.html b/dom/security/test/mixedcontentblocker/test_main.html
new file mode 100644
index 000000000..d2bc9dc7e
--- /dev/null
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -0,0 +1,187 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests for Mixed Content Blocker
+https://bugzilla.mozilla.org/show_bug.cgi?id=62178
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 62178</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
+
+ var counter = 0;
+ var settings = [ [true, true], [true, false], [false, true], [false, false] ];
+
+ var blockActive;
+ var blockDisplay;
+
+ //Cycle through 4 different preference settings.
+ function changePrefs(otherPrefs, callback) {
+ let basePrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
+ ["security.mixed_content.block_active_content", settings[counter][1]]];
+ let newPrefs = basePrefs.concat(otherPrefs);
+
+ SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+ blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+ blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+ counter++;
+ callback();
+ });
+ }
+
+ var testsToRun = {
+ iframe: false,
+ image: false,
+ imageSrcset: false,
+ imageSrcsetFallback: false,
+ imagePicture: false,
+ imageJoinPicture: false,
+ imageLeavePicture: false,
+ script: false,
+ stylesheet: false,
+ object: false,
+ media: false,
+ xhr: false,
+ };
+
+ function log(msg) {
+ document.getElementById("log").textContent += "\n" + msg;
+ }
+
+ function reloadFrame() {
+ document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main.html"></iframe>';
+ }
+
+ function checkTestsCompleted() {
+ for (var prop in testsToRun) {
+ // some test hasn't run yet so we're not done
+ if (!testsToRun[prop])
+ return;
+ }
+ //if the testsToRun are all completed, chnage the pref and run the tests again until we have cycled through all the prefs.
+ if(counter < 4) {
+ for (var prop in testsToRun) {
+ testsToRun[prop] = false;
+ }
+ //call to change the preferences
+ changePrefs([], function() {
+ log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+ reloadFrame();
+ });
+ }
+ else {
+ SimpleTest.finish();
+ }
+ }
+
+ var firstTest = true;
+
+ function receiveMessage(event) {
+ if(firstTest) {
+ log("blockActive set to "+blockActive+", blockDisplay set to "+blockDisplay+".");
+ firstTest = false;
+ }
+
+ log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
+ // test that the load type matches the pref for this type of content
+ // (i.e. active vs. display)
+
+ switch(event.data.test) {
+
+ /* Mixed Script tests */
+ case "iframe":
+ ok(blockActive == (event.data.msg == "insecure iframe blocked"), "iframe did not follow block_active_content pref");
+ testsToRun["iframe"] = true;
+ break;
+
+ case "object":
+ ok(blockActive == (event.data.msg == "insecure object blocked"), "object did not follow block_active_content pref");
+ testsToRun["object"] = true;
+ break;
+
+ case "script":
+ ok(blockActive == (event.data.msg == "insecure script blocked"), "script did not follow block_active_content pref");
+ testsToRun["script"] = true;
+ break;
+
+ case "stylesheet":
+ ok(blockActive == (event.data.msg == "insecure stylesheet blocked"), "stylesheet did not follow block_active_content pref");
+ testsToRun["stylesheet"] = true;
+ break;
+
+ case "xhr":
+ ok(blockActive == (event.data.msg == "insecure xhr blocked"), "xhr did not follow block_active_content pref");
+ testsToRun["xhr"] = true;
+ break;
+
+ /* Mixed Display tests */
+ case "image":
+ //test that the image load matches the pref for display content
+ ok(blockDisplay == (event.data.msg == "insecure image blocked"), "image did not follow block_display_content pref");
+ testsToRun["image"] = true;
+ break;
+
+ case "media":
+ ok(blockDisplay == (event.data.msg == "insecure media blocked"), "media did not follow block_display_content pref");
+ testsToRun["media"] = true;
+ break;
+
+ /* Images using the "imageset" policy, from <img srcset> and <picture>, do not get the mixed display exception */
+ case "imageSrcset":
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcset did not follow block_active_content pref");
+ testsToRun["imageSrcset"] = true;
+ break;
+
+ case "imageSrcsetFallback":
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageSrcsetFallback did not follow block_active_content pref");
+ testsToRun["imageSrcsetFallback"] = true;
+ break;
+
+ case "imagePicture":
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imagePicture did not follow block_active_content pref");
+ testsToRun["imagePicture"] = true;
+ break;
+
+ case "imageJoinPicture":
+ ok(blockActive == (event.data.msg == "insecure image blocked"), "imageJoinPicture did not follow block_active_content pref");
+ testsToRun["imageJoinPicture"] = true;
+ break;
+
+ // Should return to mixed display mode
+ case "imageLeavePicture":
+ ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
+ testsToRun["imageLeavePicture"] = true;
+ break;
+
+ }
+ checkTestsCompleted();
+ }
+
+ function startTest() {
+ // Set prefs to use mixed-content before HSTS
+ SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false],
+ ["security.mixed_content.send_hsts_priming", false]]});
+ //Set the first set of mixed content settings and increment the counter.
+ changePrefs([], function() {
+ //listen for a messages from the mixed content test harness
+ window.addEventListener("message", receiveMessage, false);
+
+ //Kick off test
+ reloadFrame();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+</head>
+
+<body onload='startTest()'>
+ <div id="framediv"></div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/dom/security/test/moz.build b/dom/security/test/moz.build
new file mode 100644
index 000000000..ddb4e9b89
--- /dev/null
+++ b/dom/security/test/moz.build
@@ -0,0 +1,31 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPCSHELL_TESTS_MANIFESTS += [
+ 'unit/xpcshell.ini',
+]
+
+TEST_DIRS += [
+ 'gtest',
+]
+
+MOCHITEST_MANIFESTS += [
+ 'cors/mochitest.ini',
+ 'csp/mochitest.ini',
+ 'general/mochitest.ini',
+ 'mixedcontentblocker/mochitest.ini',
+ 'sri/mochitest.ini',
+]
+
+MOCHITEST_CHROME_MANIFESTS += [
+ 'general/chrome.ini',
+]
+
+BROWSER_CHROME_MANIFESTS += [
+ 'contentverifier/browser.ini',
+ 'csp/browser.ini',
+ 'hsts/browser.ini',
+]
diff --git a/dom/security/test/sri/file_bug_1271796.css b/dom/security/test/sri/file_bug_1271796.css
new file mode 100644
index 000000000..c0928f2cf
--- /dev/null
+++ b/dom/security/test/sri/file_bug_1271796.css
@@ -0,0 +1,2 @@
+/*! Simple test for bug 1271796 */
+p::before { content: "\2014"; }
diff --git a/dom/security/test/sri/iframe_csp_directive_style_imports.html b/dom/security/test/sri/iframe_csp_directive_style_imports.html
new file mode 100644
index 000000000..0abea7854
--- /dev/null
+++ b/dom/security/test/sri/iframe_csp_directive_style_imports.html
@@ -0,0 +1,6 @@
+<!-- file should be loaded (text is blue), but subsequent files shouldn't (text is red) -->
+<link rel="stylesheet" href="style_importing.css"
+ integrity="sha384-m5Q2GOhAtLrdiv6rCmxY3GjEFMVInALcdTyDnEddUUiDH2uQvJSX5GSJYQiatpTK"
+ onload="parent.postMessage('finish', '*');"
+ onerror="parent.postMessage('finish', '*');">
+<p id="text-for-import-test">blue text</p>
diff --git a/dom/security/test/sri/iframe_csp_directive_style_imports.html^headers^ b/dom/security/test/sri/iframe_csp_directive_style_imports.html^headers^
new file mode 100644
index 000000000..0a6ccba79
--- /dev/null
+++ b/dom/security/test/sri/iframe_csp_directive_style_imports.html^headers^
@@ -0,0 +1 @@
+content-security-policy: require-sri-for script style
diff --git a/dom/security/test/sri/iframe_require-sri-for_main.html b/dom/security/test/sri/iframe_require-sri-for_main.html
new file mode 100644
index 000000000..467c699c7
--- /dev/null
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html
@@ -0,0 +1,47 @@
+<script>
+ window.hasCORSLoaded = false; // set through script_crossdomain1.js
+</script>
+
+<!-- script tag cors-enabled. should be loaded -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain1.js"
+ crossorigin=""
+ integrity="sha512-9Tv2DL1fHvmPQa1RviwKleE/jq72jgxj8XGLyWn3H6Xp/qbtfK/jZINoPFAv2mf0Nn1TxhZYMFULAbzJNGkl4Q=="
+ onload="parent.postMessage('good_sriLoaded', '*');"></script>
+
+<!-- script tag cors but not using SRI. should trigger onerror -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain5.js"
+ onload="parent.postMessage('bad_nonsriLoaded', '*');"
+ onerror="parent.postMessage('good_nonsriBlocked', '*');"></script>
+
+<!-- svg:script tag with cors but not using SRI. should trigger onerror -->
+<svg xmlns="http://www.w3.org/2000/svg">
+ <script xlink:href="http://example.com/tests/dom/security/test/sri/script_crossdomain3.js"
+ onload="parent.postMessage('bad_svg_nonsriLoaded', '*');"
+ onerror="parent.postMessage('good_svg_nonsriBlocked', '*');"></script>
+ ></script>
+</svg>
+
+<!-- stylesheet with cors and integrity. it should just load fine. -->
+<link rel="stylesheet" href="style1.css"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onload="parent.postMessage('good_sriLoaded', '*');">
+
+<!-- stylesheet not using SRI, should trigger onerror -->
+<link rel="stylesheet" href="style3.css"
+ onload="parent.postMessage('bad_nonsriLoaded', '*');"
+ onerror="parent.postMessage('good_nonsriBlocked', '*');">
+
+
+<p id="black-text">black text</p>
+<script>
+ // this worker should not load,
+ // given that we can not provide integrity metadata through the constructor
+ w = new Worker("rsf_worker.js");
+ w.onerror = function(e) {
+ if (typeof w == "object") {
+ parent.postMessage("finish", '*');
+ } else {
+ parent.postMessage("error", "*")
+ }
+ }
+</script>
diff --git a/dom/security/test/sri/iframe_require-sri-for_main.html^headers^ b/dom/security/test/sri/iframe_require-sri-for_main.html^headers^
new file mode 100644
index 000000000..0a6ccba79
--- /dev/null
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html^headers^
@@ -0,0 +1 @@
+content-security-policy: require-sri-for script style
diff --git a/dom/security/test/sri/iframe_require-sri-for_no_csp.html b/dom/security/test/sri/iframe_require-sri-for_no_csp.html
new file mode 100644
index 000000000..435b32ea3
--- /dev/null
+++ b/dom/security/test/sri/iframe_require-sri-for_no_csp.html
@@ -0,0 +1,5 @@
+<script>
+ w = new Worker("rsf_csp_worker.js");
+ // use the handler function in the parent frame (test_require-sri-for_csp_directive.html)
+ w.onmessage = parent.handler;
+</script>
diff --git a/dom/security/test/sri/iframe_script_crossdomain.html b/dom/security/test/sri/iframe_script_crossdomain.html
new file mode 100644
index 000000000..a752c7e79
--- /dev/null
+++ b/dom/security/test/sri/iframe_script_crossdomain.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ window.hasCORSLoaded = false;
+ window.hasNonCORSLoaded = false;
+
+ function good_nonsriLoaded() {
+ ok(true, "Non-eligible non-SRI resource was loaded correctly.");
+ }
+ function bad_nonsriBlocked() {
+ ok(false, "Non-eligible non-SRI resources should be loaded!");
+ }
+
+ function good_nonCORSInvalidBlocked() {
+ ok(true, "A non-CORS resource with invalid metadata was correctly blocked.");
+ }
+ function bad_nonCORSInvalidLoaded() {
+ ok(false, "Non-CORS resources with invalid metadata should be blocked!");
+ }
+
+ window.onerrorCalled = false;
+ window.onloadCalled = false;
+
+ function bad_onloadCalled() {
+ window.onloadCalled = true;
+ }
+
+ function good_onerrorCalled() {
+ window.onerrorCalled = true;
+ }
+
+ function good_incorrect301Blocked() {
+ ok(true, "A non-CORS load with incorrect hash redirected to a different origin was blocked correctly.");
+ }
+ function bad_incorrect301Loaded() {
+ ok(false, "Non-CORS loads with incorrect hashes redirecting to a different origin should be blocked!");
+ }
+
+ function good_correct301Blocked() {
+ ok(true, "A non-CORS load with correct hash redirected to a different origin was blocked correctly.");
+ }
+ function bad_correct301Loaded() {
+ ok(false, "Non-CORS loads with correct hashes redirecting to a different origin should be blocked!");
+ }
+
+ function good_correctDataLoaded() {
+ ok(true, "Since data: URLs are same-origin, they should be loaded.");
+ }
+ function bad_correctDataBlocked() {
+ todo(false, "We should not block scripts in data: URIs!");
+ }
+ function good_correctDataCORSLoaded() {
+ ok(true, "A data: URL with a CORS load was loaded correctly.");
+ }
+ function bad_correctDataCORSBlocked() {
+ ok(false, "We should not BLOCK scripts!");
+ }
+
+ window.onload = function() {
+ SimpleTest.finish()
+ }
+</script>
+
+<!-- cors-enabled. should be loaded -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain1.js"
+ crossorigin=""
+ integrity="sha512-9Tv2DL1fHvmPQa1RviwKleE/jq72jgxj8XGLyWn3H6Xp/qbtfK/jZINoPFAv2mf0Nn1TxhZYMFULAbzJNGkl4Q=="></script>
+
+<!-- not cors-enabled. should be blocked -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain2.js"
+ crossorigin="anonymous"
+ integrity="sha256-ntgU2U1xv7HfK1XWMTSWz6vJkyVtGzMrIAxQkux1I94="
+ onload="bad_onloadCalled()"
+ onerror="good_onerrorCalled()"></script>
+
+<!-- non-cors but not actually using SRI. should trigger onload -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain3.js"
+ integrity=" "
+ onload="good_nonsriLoaded()"
+ onerror="bad_nonsriBlocked()"></script>
+
+<!-- non-cors with invalid metadata -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain4.js"
+ integrity="sha256-bogus"
+ onload="bad_nonCORSInvalidLoaded()"
+ onerror="good_nonCORSInvalidBlocked()"></script>
+
+<!-- non-cors that's same-origin initially but redirected to another origin -->
+<script src="script_301.js"
+ integrity="sha384-invalid"
+ onerror="good_incorrect301Blocked()"
+ onload="bad_incorrect301Loaded()"></script>
+
+<!-- non-cors that's same-origin initially but redirected to another origin -->
+<script src="script_301.js"
+ integrity="sha384-1NpiDI6decClMaTWSCAfUjTdx1BiOffsCPgH4lW5hCLwmHk0VyV/g6B9Sw2kD2K3"
+ onerror="good_correct301Blocked()"
+ onload="bad_correct301Loaded()"></script>
+
+<!-- data: URLs are same-origin -->
+<script src="data:,console.log('data:valid');"
+ integrity="sha256-W5I4VIN+mCwOfR9kDbvWoY1UOVRXIh4mKRN0Nz0ookg="
+ onerror="bad_correctDataBlocked()"
+ onload="good_correctDataLoaded()"></script>
+
+<!-- not cors-enabled with data: URLs. should trigger onload -->
+<script src="data:,console.log('data:valid');"
+ crossorigin="anonymous"
+ integrity="sha256-W5I4VIN+mCwOfR9kDbvWoY1UOVRXIh4mKRN0Nz0ookg="
+ onerror="bad_correctDataCORSBlocked()"
+ onload="good_correctDataCORSLoaded()"></script>
+
+<script>
+ ok(window.hasCORSLoaded, "CORS-enabled resource with a correct hash");
+ ok(!window.hasNonCORSLoaded, "Correct hash, but non-CORS, should be blocked");
+ ok(!window.onloadCalled, "Failed loads should not call onload when they're cross-domain");
+ ok(window.onerrorCalled, "Failed loads should call onerror when they're cross-domain");
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sri/iframe_script_sameorigin.html b/dom/security/test/sri/iframe_script_sameorigin.html
new file mode 100644
index 000000000..b891db547
--- /dev/null
+++ b/dom/security/test/sri/iframe_script_sameorigin.html
@@ -0,0 +1,249 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ SimpleTest.finish();
+ }
+ </script>
+ <script>
+ function good_correctHashLoaded() {
+ ok(true, "A script was correctly loaded when integrity matched")
+ }
+ function bad_correctHashBlocked() {
+ ok(false, "We should load scripts with hashes that match!");
+ }
+
+ function good_correctHashArrayLoaded() {
+ ok(true, "A script was correctly loaded when one of the hashes in the integrity attribute matched")
+ }
+ function bad_correctHashArrayBlocked() {
+ ok(false, "We should load scripts with at least one hash that match!");
+ }
+
+ function good_emptyIntegrityLoaded() {
+ ok(true, "A script was correctly loaded when the integrity attribute was empty")
+ }
+ function bad_emptyIntegrityBlocked() {
+ ok(false, "We should load scripts with empty integrity attributes!");
+ }
+
+ function good_whitespaceIntegrityLoaded() {
+ ok(true, "A script was correctly loaded when the integrity attribute only contained whitespace")
+ }
+ function bad_whitespaceIntegrityBlocked() {
+ ok(false, "We should load scripts with integrity attributes containing only whitespace!");
+ }
+
+ function good_incorrectHashBlocked() {
+ ok(true, "A script was correctly blocked, because the hash digest was wrong");
+ }
+ function bad_incorrectHashLoaded() {
+ ok(false, "We should not load scripts with hashes that do not match the content!");
+ }
+
+ function good_incorrectHashArrayBlocked() {
+ ok(true, "A script was correctly blocked, because all the hashes were wrong");
+ }
+ function bad_incorrectHashArrayLoaded() {
+ ok(false, "We should not load scripts when none of the hashes match the content!");
+ }
+
+ function good_incorrectHashLengthBlocked() {
+ ok(true, "A script was correctly blocked, because the hash length was wrong");
+ }
+ function bad_incorrectHashLengthLoaded() {
+ ok(false, "We should not load scripts with hashes that don't have the right length!");
+ }
+
+ function bad_incorrectHashFunctionBlocked() {
+ ok(false, "We should load scripts with invalid/unsupported hash functions!");
+ }
+ function good_incorrectHashFunctionLoaded() {
+ ok(true, "A script was correctly loaded, despite the hash function being invalid/unsupported.");
+ }
+
+ function bad_missingHashFunctionBlocked() {
+ ok(false, "We should load scripts with missing hash functions!");
+ }
+ function good_missingHashFunctionLoaded() {
+ ok(true, "A script was correctly loaded, despite a missing hash function.");
+ }
+
+ function bad_missingHashValueBlocked() {
+ ok(false, "We should load scripts with missing hash digests!");
+ }
+ function good_missingHashValueLoaded() {
+ ok(true, "A script was correctly loaded, despite the missing hash digest.");
+ }
+
+ function good_401Blocked() {
+ ok(true, "A script was not loaded because of 401 response.");
+ }
+ function bad_401Loaded() {
+ ok(false, "We should nt load scripts with a 401 response!");
+ }
+
+ function good_valid302Loaded() {
+ ok(true, "A script was loaded successfully despite a 302 response.");
+ }
+ function bad_valid302Blocked() {
+ ok(false, "We should load scripts with a 302 response and the right hash!");
+ }
+
+ function good_invalid302Blocked() {
+ ok(true, "A script was blocked successfully after a 302 response.");
+ }
+ function bad_invalid302Loaded() {
+ ok(false, "We should not load scripts with a 302 response and the wrong hash!");
+ }
+
+ function good_validBlobLoaded() {
+ ok(true, "A script was loaded successfully from a blob: URL.");
+ }
+ function bad_validBlobBlocked() {
+ ok(false, "We should load scripts using blob: URLs with the right hash!");
+ }
+
+ function good_invalidBlobBlocked() {
+ ok(true, "A script was blocked successfully from a blob: URL.");
+ }
+ function bad_invalidBlobLoaded() {
+ ok(false, "We should not load scripts using blob: URLs with the wrong hash!");
+ }
+</script>
+</head>
+<body>
+ <!-- valid hash. should trigger onload -->
+ <!-- the hash value comes from running this command:
+ cat script.js | openssl dgst -sha256 -binary | openssl enc -base64 -A
+ -->
+ <script src="script.js"
+ integrity="sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_correctHashBlocked()"
+ onload="good_correctHashLoaded()"></script>
+
+ <!-- valid sha512 hash. should trigger onload -->
+ <script src="script.js"
+ integrity="sha512-mzSqH+vC6qrXX46JX2WEZ0FtY/lGj/5+5yYCBlk0jfYHLm0vP6XgsURbq83mwMApsnwbDLXdgjp5J8E93GT6Mw==?ignore=this"
+ onerror="bad_correctHashBlocked()"
+ onload="good_correctHashLoaded()"></script>
+
+ <!-- one valid sha256 hash. should trigger onload -->
+ <script src="script.js"
+ integrity="sha256-rkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA= sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA= sha256-rkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_correctHashArrayBlocked()"
+ onload="good_correctHashArrayLoaded()"></script>
+
+ <!-- empty integrity. should trigger onload -->
+ <script src="script.js"
+ integrity=""
+ onerror="bad_emptyIntegrityBlocked()"
+ onload="good_emptyIntegrityLoaded()"></script>
+
+ <!-- whitespace integrity. should trigger onload -->
+ <script src="script.js"
+ integrity="
+
+"
+ onerror="bad_whitespaceIntegrityBlocked()"
+ onload="good_whitespaceIntegrityLoaded()"></script>
+
+ <!-- invalid sha256 hash but valid sha384 hash. should trigger onload -->
+ <script src="script.js"
+ integrity="sha256-bogus sha384-zDCkvKOHXk8mM6Nk07oOGXGME17PA4+ydFw+hq0r9kgF6ZDYFWK3fLGPEy7FoOAo?"
+ onerror="bad_correctHashBlocked()"
+ onload="good_correctHashLoaded()"></script>
+
+ <!-- valid sha256 and invalid sha384. should trigger onerror -->
+ <script src="script.js"
+ integrity="sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA= sha384-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="good_incorrectHashLengthBlocked()"
+ onload="bad_incorrectHashLengthLoaded()"></script>
+
+ <!-- invalid hash. should trigger onerror -->
+ <script src="script.js"
+ integrity="sha256-rkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="good_incorrectHashBlocked()"
+ onload="bad_incorrectHashLoaded()"></script>
+
+ <!-- invalid hashes. should trigger onerror -->
+ <script src="script.js"
+ integrity="sha256-rkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA= sha256-ZkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA= sha256-zkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="good_incorrectHashBlocked()"
+ onload="bad_incorrectHashLoaded()"></script>
+
+ <!-- invalid hash function. should trigger onload -->
+ <script src="script.js"
+ integrity="rot13-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_incorrectHashFunctionBlocked()"
+ onload="good_incorrectHashFunctionLoaded()"></script>
+
+ <!-- missing hash function. should trigger onload -->
+ <script src="script.js"
+ integrity="RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_missingHashFunctionBlocked()"
+ onload="good_missingHashFunctionLoaded()"></script>
+
+ <!-- missing hash value. should trigger onload -->
+ <script src="script.js"
+ integrity="sha512-"
+ onerror="bad_missingHashValueBlocked()"
+ onload="good_missingHashValueLoaded()"></script>
+
+ <!-- 401 response. should trigger onerror -->
+ <script src="script_401.js"
+ integrity="sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="good_401Blocked()"
+ onload="bad_401Loaded()"></script>
+
+ <!-- valid sha256 after a redirection. should trigger onload -->
+ <script src="script_302.js"
+ integrity="sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_valid302Blocked()"
+ onload="good_valid302Loaded()"></script>
+
+ <!-- invalid sha256 after a redirection. should trigger onerror -->
+ <script src="script_302.js"
+ integrity="sha256-JSi74NSN8WQNr9syBGmNg2APJp9PnHUO5ioZo5hmIiQ="
+ onerror="good_invalid302Blocked()"
+ onload="bad_invalid302Loaded()"></script>
+
+ <!-- valid sha256 for a blob: URL -->
+ <script>
+ var blob = new Blob(["console.log('blob:valid');"],
+ {type:"application/javascript"});
+ var script = document.createElement('script');
+ script.setAttribute('src', URL.createObjectURL(blob));
+ script.setAttribute('integrity', 'sha256-AwLdXiGfCqOxOXDPUim73G8NVEL34jT0IcQR/tqv/GQ=');
+ script.onerror = bad_validBlobBlocked;
+ script.onload = good_validBlobLoaded;
+ var head = document.getElementsByTagName('head').item(0);
+ head.appendChild(script);
+ </script>
+
+ <!-- invalid sha256 for a blob: URL -->
+ <script>
+ var blob = new Blob(["console.log('blob:invalid');"],
+ {type:"application/javascript"});
+ var script = document.createElement('script');
+ script.setAttribute('src', URL.createObjectURL(blob));
+ script.setAttribute('integrity', 'sha256-AwLdXiGfCqOxOXDPUim73G8NVEL34jT0IcQR/tqv/GQ=');
+ script.onerror = good_invalidBlobBlocked;
+ script.onload = bad_invalidBlobLoaded;
+ var head = document.getElementsByTagName('head').item(0);
+ head.appendChild(script);
+ </script>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/sri/iframe_sri_disabled.html b/dom/security/test/sri/iframe_sri_disabled.html
new file mode 100644
index 000000000..9fb10293a
--- /dev/null
+++ b/dom/security/test/sri/iframe_sri_disabled.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ SimpleTest.finish();
+ }
+ </script>
+ <script>
+ function good_correctHashLoaded() {
+ ok(true, "A script was correctly loaded when integrity matched")
+ }
+ function bad_correctHashBlocked() {
+ ok(false, "We should load scripts with hashes that match!");
+ }
+
+ function good_incorrectHashLoaded() {
+ ok(true, "A script was correctly loaded despite the incorrect hash because SRI is disabled.");
+ }
+ function bad_incorrectHashBlocked() {
+ ok(false, "We should load scripts with hashes that do not match the content when SRI is disabled!");
+ }
+
+ function good_correctStyleHashLoaded() {
+ ok(true, "A stylesheet was correctly loaded when integrity matched")
+ }
+ function bad_correctStyleHashBlocked() {
+ ok(false, "We should load stylesheets with hashes that match!");
+ }
+
+ function good_incorrectStyleHashLoaded() {
+ ok(true, "A stylesheet was correctly loaded despite the incorrect hash because SRI is disabled.");
+ }
+ function bad_incorrectStyleHashBlocked() {
+ ok(false, "We should load stylesheets with hashes that do not match the content when SRI is disabled!");
+ }
+ </script>
+
+ <!-- valid sha256 hash. should trigger onload -->
+ <link rel="stylesheet" href="style1.css?disabled"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onerror="bad_correctStyleHashBlocked()"
+ onload="good_correctStyleHashLoaded()">
+
+ <!-- invalid sha256 hash. should trigger onerror -->
+ <link rel="stylesheet" href="style2.css?disabled"
+ integrity="sha256-bogus"
+ onerror="bad_incorrectStyleHashBlocked()"
+ onload="good_incorrectStyleHashLoaded()">
+</head>
+<body>
+ <!-- valid hash. should trigger onload -->
+ <script src="script.js"
+ integrity="sha256-RkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_correctHashBlocked()"
+ onload="good_correctHashLoaded()"></script>
+
+ <!-- invalid hash. should trigger onerror -->
+ <script src="script.js"
+ integrity="sha256-rkrQYrxD/HCx+ImVLb51nvxJ6ZHfwuEm7bHppTun9oA="
+ onerror="bad_incorrectHashBlocked()"
+ onload="good_incorrectHashLoaded()"></script>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/sri/iframe_style_crossdomain.html b/dom/security/test/sri/iframe_style_crossdomain.html
new file mode 100644
index 000000000..22c8593b5
--- /dev/null
+++ b/dom/security/test/sri/iframe_style_crossdomain.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ function check_styles() {
+ var redText = document.getElementById('red-text');
+ var greenText = document.getElementById('green-text');
+ var blueText = document.getElementById('blue-text');
+ var redTextColor = window.getComputedStyle(redText, null).getPropertyValue('color');
+ var greenTextColor = window.getComputedStyle(greenText, null).getPropertyValue('color');
+ var blueTextColor = window.getComputedStyle(blueText, null).getPropertyValue('color');
+ ok(redTextColor == 'rgb(255, 0, 0)', "The first part should be red.");
+ ok(greenTextColor == 'rgb(0, 255, 0)', "The second part should be green.");
+ ok(blueTextColor == 'rgb(0, 0, 255)', "The third part should be blue.");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ check_styles();
+ SimpleTest.finish();
+ }
+ </script>
+ <script>
+ function good_correctHashCORSLoaded() {
+ ok(true, "A CORS cross-domain stylesheet with correct hash was correctly loaded.");
+ }
+ function bad_correctHashCORSBlocked() {
+ ok(false, "We should load CORS cross-domain stylesheets with hashes that match!");
+ }
+ function good_correctHashBlocked() {
+ ok(true, "A non-CORS cross-domain stylesheet with correct hash was correctly blocked.");
+ }
+ function bad_correctHashLoaded() {
+ ok(false, "We should block non-CORS cross-domain stylesheets with hashes that match!");
+ }
+
+ function good_incorrectHashBlocked() {
+ ok(true, "A non-CORS cross-domain stylesheet with incorrect hash was correctly blocked.");
+ }
+ function bad_incorrectHashLoaded() {
+ ok(false, "We should load non-CORS cross-domain stylesheets with incorrect hashes!");
+ }
+
+ function bad_correctDataBlocked() {
+ ok(false, "We should not block non-CORS cross-domain stylesheets in data: URI!");
+ }
+ function good_correctDataLoaded() {
+ ok(true, "A non-CORS cross-domain stylesheet with data: URI was correctly loaded.");
+ }
+ function bad_correctDataCORSBlocked() {
+ ok(false, "We should not block CORS stylesheets in data: URI!");
+ }
+ function good_correctDataCORSLoaded() {
+ ok(true, "A CORS stylesheet with data: URI was correctly loaded.");
+ }
+
+ function good_correctHashOpaqueBlocked() {
+ ok(true, "A non-CORS(Opaque) cross-domain stylesheet with correct hash was correctly blocked.");
+ }
+ function bad_correctHashOpaqueLoaded() {
+ ok(false, "We should not load non-CORS(Opaque) cross-domain stylesheets with correct hashes!");
+ }
+ </script>
+
+ <!-- valid CORS sha256 hash -->
+ <link rel="stylesheet" href="http://example.com/tests/dom/security/test/sri/style1.css"
+ crossorigin="anonymous"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onerror="bad_correctHashCORSBlocked()"
+ onload="good_correctHashCORSLoaded()">
+
+ <!-- valid non-CORS sha256 hash -->
+ <link rel="stylesheet" href="style_301.css"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onerror="good_correctHashBlocked()"
+ onload="bad_correctHashLoaded()">
+
+ <!-- invalid non-CORS sha256 hash -->
+ <link rel="stylesheet" href="style_301.css?again"
+ integrity="sha256-bogus"
+ onerror="good_incorrectHashBlocked()"
+ onload="bad_incorrectHashLoaded()">
+
+ <!-- valid non-CORS sha256 hash in a data: URL -->
+ <link rel="stylesheet" href="data:text/css,.green-text{color:rgb(0, 255, 0)}"
+ integrity="sha256-EhVtGGyovvffvYdhyqJxUJ/ekam7zlxxo46iM13cwP0="
+ onerror="bad_correctDataBlocked()"
+ onload="good_correctDataLoaded()">
+
+ <!-- valid CORS sha256 hash in a data: URL -->
+ <link rel="stylesheet" href="data:text/css,.blue-text{color:rgb(0, 0, 255)}"
+ crossorigin="anonymous"
+ integrity="sha256-m0Fs2hNSyPOn1030Dp+c8pJFHNmwpeTbB+8J/DcqLss="
+ onerror="bad_correctDataCORSBlocked()"
+ onload="good_correctDataCORSLoaded()">
+
+ <!-- valid non-CORS sha256 hash -->
+ <link rel="stylesheet" href="http://example.com/tests/dom/security/test/sri/style1.css"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onerror="good_correctHashOpaqueBlocked()"
+ onload="bad_correctHashOpaqueLoaded()">
+</head>
+<body>
+<p><span id="red-text">This should be red</span> but
+ <span id="green-text" class="green-text">this should be green</span> and
+ <span id="blue-text" class="blue-text">this should be blue</span></p>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/sri/iframe_style_sameorigin.html b/dom/security/test/sri/iframe_style_sameorigin.html
new file mode 100644
index 000000000..d994e7c2b
--- /dev/null
+++ b/dom/security/test/sri/iframe_style_sameorigin.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ function check_styles() {
+ var redText = document.getElementById('red-text');
+ var blueText = document.getElementById('blue-text-element');
+ var blackText1 = document.getElementById('black-text');
+ var blackText2 = document.getElementById('black-text-2');
+ var redTextColor = window.getComputedStyle(redText, null).getPropertyValue('color');
+ var blueTextColor = window.getComputedStyle(blueText, null).getPropertyValue('color');
+ var blackTextColor1 = window.getComputedStyle(blackText1, null).getPropertyValue('color');
+ var blackTextColor2 = window.getComputedStyle(blackText2, null).getPropertyValue('color');
+ ok(redTextColor == 'rgb(255, 0, 0)', "The first part should be red.");
+ ok(blueTextColor == 'rgb(0, 0, 255)', "The second part should be blue.");
+ ok(blackTextColor1 == 'rgb(0, 0, 0)', "The second last part should still be black.");
+ ok(blackTextColor2 == 'rgb(0, 0, 0)', "The last part should still be black.");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ check_styles();
+ SimpleTest.finish();
+ }
+ </script>
+ <script>
+ function good_correctHashLoaded() {
+ ok(true, "A stylesheet was correctly loaded when integrity matched");
+ }
+ function bad_correctHashBlocked() {
+ ok(false, "We should load stylesheets with hashes that match!");
+ }
+
+ function good_emptyIntegrityLoaded() {
+ ok(true, "A stylesheet was correctly loaded when the integrity attribute was empty");
+ }
+ function bad_emptyIntegrityBlocked() {
+ ok(false, "We should load stylesheets with empty integrity attributes!");
+ }
+
+ function good_incorrectHashBlocked() {
+ ok(true, "A stylesheet was correctly blocked, because the hash digest was wrong");
+ }
+ function bad_incorrectHashLoaded() {
+ ok(false, "We should not load stylesheets with hashes that do not match the content!");
+ }
+
+ function good_validBlobLoaded() {
+ ok(true, "A stylesheet was loaded successfully from a blob: URL with the right hash.");
+ }
+ function bad_validBlobBlocked() {
+ ok(false, "We should load stylesheets using blob: URLs with the right hash!");
+ }
+ function good_invalidBlobBlocked() {
+ ok(true, "A stylesheet was blocked successfully from a blob: URL with an invalid hash.");
+ }
+ function bad_invalidBlobLoaded() {
+ ok(false, "We should not load stylesheets using blob: URLs when they have the wrong hash!");
+ }
+
+ function good_correctUTF8HashLoaded() {
+ ok(true, "A UTF8 stylesheet was correctly loaded when integrity matched");
+ }
+ function bad_correctUTF8HashBlocked() {
+ ok(false, "We should load UTF8 stylesheets with hashes that match!");
+ }
+ function good_correctUTF8BOMHashLoaded() {
+ ok(true, "A UTF8 stylesheet (with BOM) was correctly loaded when integrity matched");
+ }
+ function bad_correctUTF8BOMHashBlocked() {
+ ok(false, "We should load UTF8 (with BOM) stylesheets with hashes that match!");
+ }
+ function good_correctUTF8ishHashLoaded() {
+ ok(true, "A UTF8ish stylesheet was correctly loaded when integrity matched");
+ }
+ function bad_correctUTF8ishHashBlocked() {
+ ok(false, "We should load UTF8ish stylesheets with hashes that match!");
+ }
+ </script>
+
+ <!-- valid sha256 hash. should trigger onload -->
+ <link rel="stylesheet" href="style1.css"
+ integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+ onerror="bad_correctHashBlocked()"
+ onload="good_correctHashLoaded()">
+
+ <!-- empty metadata. should trigger onload -->
+ <link rel="stylesheet" href="style2.css"
+ integrity=""
+ onerror="bad_emptyIntegrityBlocked()"
+ onload="good_emptyIntegrityLoaded()">
+
+ <!-- invalid sha256 hash. should trigger onerror -->
+ <link rel="stylesheet" href="style3.css"
+ integrity="sha256-bogus"
+ onerror="good_incorrectHashBlocked()"
+ onload="bad_incorrectHashLoaded()">
+
+ <!-- valid sha384 hash of a utf8 file. should trigger onload -->
+ <link rel="stylesheet" href="style4.css"
+ integrity="sha384-13rt+j7xMDLhohLukb7AZx8lDGS3hkahp0IoeuyvxSNVPyc1QQmTDcwXGhQZjoMH"
+ onerror="bad_correctUTF8HashBlocked()"
+ onload="good_correctUTF8HashLoaded()">
+
+ <!-- valid sha384 hash of a utf8 file with a BOM. should trigger onload -->
+ <link rel="stylesheet" href="style5.css"
+ integrity="sha384-udAqVKPIHf/OD1isAYKrgzsog/3Q6lSEL2nKhtLSTmHryiae0+y6x1akeTzEF446"
+ onerror="bad_correctUTF8BOMHashBlocked()"
+ onload="good_correctUTF8BOMHashLoaded()">
+
+ <!-- valid sha384 hash of a utf8 file with the wrong charset. should trigger onload -->
+ <link rel="stylesheet" href="style6.css"
+ integrity="sha384-Xli4ROFoVGCiRgXyl7y8jv5Vm2yuqj+8tkNL3cUI7AHaCocna75JLs5xID437W6C"
+ onerror="bad_correctUTF8ishHashBlocked()"
+ onload="good_correctUTF8ishHashLoaded()">
+</head>
+<body>
+
+<!-- valid sha256 for a blob: URL -->
+<script>
+ var blob = new Blob(['.blue-text{color:blue}'],
+ {type: 'text/css'});
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = window.URL.createObjectURL(blob);
+ link.setAttribute('integrity', 'sha256-/F+EMVnTWYJOAzN5n7/21idiydu6nRi33LZOISZtwOM=');
+ link.onerror = bad_validBlobBlocked;
+ link.onload = good_validBlobLoaded;
+ document.body.appendChild(link);
+</script>
+
+<!-- invalid sha256 for a blob: URL -->
+<script>
+ var blob = new Blob(['.black-text{color:blue}'],
+ {type: 'text/css'});
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = window.URL.createObjectURL(blob);
+ link.setAttribute('integrity', 'sha256-/F+EMVnTWYJOAzN5n7/21idiydu6nRi33LZOISZtwOM=');
+ link.onerror = good_invalidBlobBlocked;
+ link.onload = bad_invalidBlobLoaded;
+ document.body.appendChild(link);
+</script>
+
+<p><span id="red-text">This should be red </span>,
+ <span id="purple-text">this should be purple</span>,
+ <span id="brown-text">this should be brown</span>,
+ <span id="orange-text">this should be orange</span>, and
+ <span class="blue-text" id="blue-text-element">this should be blue.</span>
+ However, <span id="black-text">this should stay black</span> and
+ <span class="black-text" id="black-text-2">this should also stay black.</span>
+</p>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/sri/mochitest.ini b/dom/security/test/sri/mochitest.ini
new file mode 100644
index 000000000..e6e2e2fc3
--- /dev/null
+++ b/dom/security/test/sri/mochitest.ini
@@ -0,0 +1,57 @@
+[DEFAULT]
+support-files =
+ file_bug_1271796.css
+ iframe_csp_directive_style_imports.html
+ iframe_csp_directive_style_imports.html^headers^
+ iframe_require-sri-for_main.html
+ iframe_require-sri-for_main.html^headers^
+ iframe_require-sri-for_no_csp.html
+ iframe_script_crossdomain.html
+ iframe_script_sameorigin.html
+ iframe_sri_disabled.html
+ iframe_style_crossdomain.html
+ iframe_style_sameorigin.html
+ rsf_csp_worker.js
+ rsf_csp_worker.js^headers^
+ rsf_imported.js
+ rsf_worker.js
+ script_crossdomain1.js
+ script_crossdomain1.js^headers^
+ script_crossdomain2.js
+ script_crossdomain3.js
+ script_crossdomain3.js^headers^
+ script_crossdomain4.js
+ script_crossdomain4.js^headers^
+ script_crossdomain5.js
+ script_crossdomain5.js^headers^
+ script.js
+ script.js^headers^
+ script_301.js
+ script_301.js^headers^
+ script_302.js
+ script_302.js^headers^
+ script_401.js
+ script_401.js^headers^
+ style1.css
+ style1.css^headers^
+ style2.css
+ style3.css
+ style4.css
+ style4.css^headers^
+ style5.css
+ style6.css
+ style6.css^headers^
+ style_301.css
+ style_301.css^headers^
+ style_importing.css
+ style_imported.css
+
+[test_script_sameorigin.html]
+[test_script_crossdomain.html]
+[test_sri_disabled.html]
+[test_style_crossdomain.html]
+[test_style_sameorigin.html]
+[test_require-sri-for_csp_directive.html]
+[test_require-sri-for_csp_directive_disabled.html]
+[test_bug_1271796.html]
+[test_csp_directive_style_imports.html]
diff --git a/dom/security/test/sri/rsf_csp_worker.js b/dom/security/test/sri/rsf_csp_worker.js
new file mode 100644
index 000000000..553fdf92b
--- /dev/null
+++ b/dom/security/test/sri/rsf_csp_worker.js
@@ -0,0 +1,9 @@
+postMessage("good_worker_could_load");
+try {
+ importScripts('rsf_imported.js');
+} catch(e) {
+ postMessage("good_worker_after_importscripts");
+}
+finally {
+ postMessage("finish");
+}
diff --git a/dom/security/test/sri/rsf_csp_worker.js^headers^ b/dom/security/test/sri/rsf_csp_worker.js^headers^
new file mode 100644
index 000000000..0a6ccba79
--- /dev/null
+++ b/dom/security/test/sri/rsf_csp_worker.js^headers^
@@ -0,0 +1 @@
+content-security-policy: require-sri-for script style
diff --git a/dom/security/test/sri/rsf_imported.js b/dom/security/test/sri/rsf_imported.js
new file mode 100644
index 000000000..33f54b7a0
--- /dev/null
+++ b/dom/security/test/sri/rsf_imported.js
@@ -0,0 +1 @@
+postMessage('bad_worker_could_load_via_importScripts');
diff --git a/dom/security/test/sri/rsf_spawn_CSPd_worker.js b/dom/security/test/sri/rsf_spawn_CSPd_worker.js
new file mode 100644
index 000000000..652c77100
--- /dev/null
+++ b/dom/security/test/sri/rsf_spawn_CSPd_worker.js
@@ -0,0 +1,3 @@
+w = new Worker("rsf_csp_worker.js");
+// use the handler function in test_require-sri-for_csp_directive.html
+w.onmessage = parent.handler;
diff --git a/dom/security/test/sri/rsf_worker.js b/dom/security/test/sri/rsf_worker.js
new file mode 100644
index 000000000..553d3deee
--- /dev/null
+++ b/dom/security/test/sri/rsf_worker.js
@@ -0,0 +1,2 @@
+parent.postMessage('bad_worker_could_load', '*');
+importScripts('rsf_imported.js');
diff --git a/dom/security/test/sri/script.js b/dom/security/test/sri/script.js
new file mode 100644
index 000000000..8fd8f96b2
--- /dev/null
+++ b/dom/security/test/sri/script.js
@@ -0,0 +1 @@
+var load=true;
diff --git a/dom/security/test/sri/script.js^headers^ b/dom/security/test/sri/script.js^headers^
new file mode 100644
index 000000000..b77232d81
--- /dev/null
+++ b/dom/security/test/sri/script.js^headers^
@@ -0,0 +1 @@
+Cache-control: public
diff --git a/dom/security/test/sri/script_301.js b/dom/security/test/sri/script_301.js
new file mode 100644
index 000000000..9a95de77c
--- /dev/null
+++ b/dom/security/test/sri/script_301.js
@@ -0,0 +1 @@
+var load=false;
diff --git a/dom/security/test/sri/script_301.js^headers^ b/dom/security/test/sri/script_301.js^headers^
new file mode 100644
index 000000000..efbfb7334
--- /dev/null
+++ b/dom/security/test/sri/script_301.js^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: http://example.com/tests/dom/security/test/sri/script_crossdomain5.js
diff --git a/dom/security/test/sri/script_302.js b/dom/security/test/sri/script_302.js
new file mode 100644
index 000000000..9a95de77c
--- /dev/null
+++ b/dom/security/test/sri/script_302.js
@@ -0,0 +1 @@
+var load=false;
diff --git a/dom/security/test/sri/script_302.js^headers^ b/dom/security/test/sri/script_302.js^headers^
new file mode 100644
index 000000000..05a545a6a
--- /dev/null
+++ b/dom/security/test/sri/script_302.js^headers^
@@ -0,0 +1,2 @@
+HTTP 302 Found
+Location: /tests/dom/security/test/sri/script.js
diff --git a/dom/security/test/sri/script_401.js b/dom/security/test/sri/script_401.js
new file mode 100644
index 000000000..8fd8f96b2
--- /dev/null
+++ b/dom/security/test/sri/script_401.js
@@ -0,0 +1 @@
+var load=true;
diff --git a/dom/security/test/sri/script_401.js^headers^ b/dom/security/test/sri/script_401.js^headers^
new file mode 100644
index 000000000..889fbe081
--- /dev/null
+++ b/dom/security/test/sri/script_401.js^headers^
@@ -0,0 +1,2 @@
+HTTP 401 Authorization Required
+Cache-control: public
diff --git a/dom/security/test/sri/script_crossdomain1.js b/dom/security/test/sri/script_crossdomain1.js
new file mode 100644
index 000000000..1f17a6db2
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain1.js
@@ -0,0 +1,4 @@
+/*
+ * this file should be loaded, because it has CORS enabled.
+*/
+window.hasCORSLoaded = true;
diff --git a/dom/security/test/sri/script_crossdomain1.js^headers^ b/dom/security/test/sri/script_crossdomain1.js^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain1.js^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/security/test/sri/script_crossdomain2.js b/dom/security/test/sri/script_crossdomain2.js
new file mode 100644
index 000000000..4b0208ab3
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain2.js
@@ -0,0 +1,5 @@
+/*
+ * this file should not be loaded, because it does not have CORS
+ * enabled.
+ */
+window.hasNonCORSLoaded = true;
diff --git a/dom/security/test/sri/script_crossdomain3.js b/dom/security/test/sri/script_crossdomain3.js
new file mode 100644
index 000000000..eed05d59b
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain3.js
@@ -0,0 +1 @@
+// This script intentionally left blank
diff --git a/dom/security/test/sri/script_crossdomain3.js^headers^ b/dom/security/test/sri/script_crossdomain3.js^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain3.js^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/security/test/sri/script_crossdomain4.js b/dom/security/test/sri/script_crossdomain4.js
new file mode 100644
index 000000000..eed05d59b
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain4.js
@@ -0,0 +1 @@
+// This script intentionally left blank
diff --git a/dom/security/test/sri/script_crossdomain4.js^headers^ b/dom/security/test/sri/script_crossdomain4.js^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain4.js^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/security/test/sri/script_crossdomain5.js b/dom/security/test/sri/script_crossdomain5.js
new file mode 100644
index 000000000..eed05d59b
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain5.js
@@ -0,0 +1 @@
+// This script intentionally left blank
diff --git a/dom/security/test/sri/script_crossdomain5.js^headers^ b/dom/security/test/sri/script_crossdomain5.js^headers^
new file mode 100644
index 000000000..cb762eff8
--- /dev/null
+++ b/dom/security/test/sri/script_crossdomain5.js^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/dom/security/test/sri/style1.css b/dom/security/test/sri/style1.css
new file mode 100644
index 000000000..c7ab9ecff
--- /dev/null
+++ b/dom/security/test/sri/style1.css
@@ -0,0 +1,3 @@
+#red-text {
+ color: red;
+}
diff --git a/dom/security/test/sri/style1.css^headers^ b/dom/security/test/sri/style1.css^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/security/test/sri/style1.css^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/security/test/sri/style2.css b/dom/security/test/sri/style2.css
new file mode 100644
index 000000000..9eece75e5
--- /dev/null
+++ b/dom/security/test/sri/style2.css
@@ -0,0 +1 @@
+; A valid but somewhat uninteresting stylesheet
diff --git a/dom/security/test/sri/style3.css b/dom/security/test/sri/style3.css
new file mode 100644
index 000000000..b64fa3b74
--- /dev/null
+++ b/dom/security/test/sri/style3.css
@@ -0,0 +1,3 @@
+#black-text {
+ color: green;
+}
diff --git a/dom/security/test/sri/style4.css b/dom/security/test/sri/style4.css
new file mode 100644
index 000000000..eab83656e
--- /dev/null
+++ b/dom/security/test/sri/style4.css
@@ -0,0 +1,4 @@
+/* François was here. */
+#purple-text {
+ color: purple;
+}
diff --git a/dom/security/test/sri/style4.css^headers^ b/dom/security/test/sri/style4.css^headers^
new file mode 100644
index 000000000..e13897f15
--- /dev/null
+++ b/dom/security/test/sri/style4.css^headers^
@@ -0,0 +1 @@
+Content-Type: text/css; charset=utf-8
diff --git a/dom/security/test/sri/style5.css b/dom/security/test/sri/style5.css
new file mode 100644
index 000000000..5d59134cc
--- /dev/null
+++ b/dom/security/test/sri/style5.css
@@ -0,0 +1,4 @@
+/* François was here. */
+#orange-text {
+ color: orange;
+}
diff --git a/dom/security/test/sri/style6.css b/dom/security/test/sri/style6.css
new file mode 100644
index 000000000..569557694
--- /dev/null
+++ b/dom/security/test/sri/style6.css
@@ -0,0 +1,4 @@
+/* François was here. */
+#brown-text {
+ color: brown;
+}
diff --git a/dom/security/test/sri/style6.css^headers^ b/dom/security/test/sri/style6.css^headers^
new file mode 100644
index 000000000..d866aa522
--- /dev/null
+++ b/dom/security/test/sri/style6.css^headers^
@@ -0,0 +1 @@
+Content-Type: text/css; charset=iso-8859-8
diff --git a/dom/security/test/sri/style_301.css b/dom/security/test/sri/style_301.css
new file mode 100644
index 000000000..c7ab9ecff
--- /dev/null
+++ b/dom/security/test/sri/style_301.css
@@ -0,0 +1,3 @@
+#red-text {
+ color: red;
+}
diff --git a/dom/security/test/sri/style_301.css^headers^ b/dom/security/test/sri/style_301.css^headers^
new file mode 100644
index 000000000..c5b78ee04
--- /dev/null
+++ b/dom/security/test/sri/style_301.css^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: http://example.com/tests/dom/security/test/sri/style1.css
diff --git a/dom/security/test/sri/style_imported.css b/dom/security/test/sri/style_imported.css
new file mode 100644
index 000000000..4469e8d78
--- /dev/null
+++ b/dom/security/test/sri/style_imported.css
@@ -0,0 +1,6 @@
+#text-for-import-test {
+ color: red;
+}
+#text-for-import-test::before {
+ content: 'Test failed';
+}
diff --git a/dom/security/test/sri/style_importing.css b/dom/security/test/sri/style_importing.css
new file mode 100644
index 000000000..985dd3467
--- /dev/null
+++ b/dom/security/test/sri/style_importing.css
@@ -0,0 +1,4 @@
+/* neither of them should load. trying multiple cases*/
+@import url("style_imported.css");
+@import 'style_imported.css';
+#text-for-import-test { color: blue; }
diff --git a/dom/security/test/sri/test_bug_1271796.html b/dom/security/test/sri/test_bug_1271796.html
new file mode 100644
index 000000000..1d639c9cb
--- /dev/null
+++ b/dom/security/test/sri/test_bug_1271796.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function good_shouldLoadEncodingProblem() {
+ ok(true, "Problematically encoded file correctly loaded.")
+ };
+ function bad_shouldntEncounterBug1271796() {
+ ok(false, "Problematically encoded should load!")
+ }
+ window.onload = function() {
+ SimpleTest.finish();
+ }
+ </script>
+ <link rel="stylesheet" href="file_bug_1271796.css" crossorigin="anonymous"
+ integrity="sha384-8Xl0mTN4S2QZ5xeliG1sd4Ar9o1xMw6JoJy9RNjyHGQDha7GiLxo8l1llwLVgTNG"
+ onload="good_shouldLoadEncodingProblem();"
+ onerror="bad_shouldntEncounterBug1271796();">
+</head>
+<body>
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1271796">Bug 1271796</a><br>
+<p>This text is prepended by emdash if css has loaded</p>
+</body>
+</html>
diff --git a/dom/security/test/sri/test_csp_directive_style_imports.html b/dom/security/test/sri/test_csp_directive_style_imports.html
new file mode 100644
index 000000000..f293e2412
--- /dev/null
+++ b/dom/security/test/sri/test_csp_directive_style_imports.html
@@ -0,0 +1,42 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SRI require-sri-for CSP directive</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265318">Mozilla Bug 1265318</a><br>
+<iframe style="width:200px;height:200px;" id="test_frame"></iframe><br>
+</body>
+<script type="application/javascript">
+ var finished = 0;
+ SpecialPowers.setBoolPref("security.csp.experimentalEnabled", true);
+ SimpleTest.waitForExplicitFinish();
+ function handler(event) {
+ console.log(event);
+ switch (event.data) {
+ case 'finish':
+ // need finish message from iframe_require-sri-for_main onload event and
+ // from iframe_require-sri-for_no_csp, which spawns a Worker
+ var importText = frame.contentDocument.getElementById('text-for-import-test');
+ var importColor = frame.contentWindow.getComputedStyle(importText, null).getPropertyValue('color');
+ ok(importColor == 'rgb(0, 0, 255)', "The import should not work without integrity. The text is now red, but should not.");
+ removeEventListener('message', handler);
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, 'Something is wrong here');
+ break;
+ }
+ }
+ addEventListener("message", handler);
+ // This frame has a CSP that requires SRI
+ var frame = document.getElementById("test_frame");
+ frame.src = "iframe_csp_directive_style_imports.html";
+</script>
+</html>
diff --git a/dom/security/test/sri/test_require-sri-for_csp_directive.html b/dom/security/test/sri/test_require-sri-for_csp_directive.html
new file mode 100644
index 000000000..ef1b3603f
--- /dev/null
+++ b/dom/security/test/sri/test_require-sri-for_csp_directive.html
@@ -0,0 +1,76 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SRI require-sri-for CSP directive</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265318">Mozilla Bug 1265318</a><br>
+<iframe style="width:200px;height:200px;" id="test_frame"></iframe><br>
+<iframe style="width:200px;height:200px;" id="test_frame_no_csp"></iframe>
+</body>
+<script type="application/javascript">
+ var finished = 0;
+ SpecialPowers.setBoolPref("security.csp.experimentalEnabled", true);
+ SimpleTest.waitForExplicitFinish();
+ function handler(event) {
+ switch (event.data) {
+ case 'good_sriLoaded':
+ ok(true, "Eligible SRI resources was correctly loaded.");
+ break;
+ case 'bad_nonsriLoaded':
+ ok(false, "Eligible non-SRI resource should be blocked by the CSP!");
+ break;
+ case 'good_nonsriBlocked':
+ ok(true, "Eligible non-SRI resources was correctly blocked by the CSP.");
+ break;
+ case 'bad_svg_nonsriLoaded':
+ ok(false, 'Eligible non-SRI resource should be blocked by the CSP.');
+ break;
+ case 'good_svg_nonsriBlocked':
+ ok(true, 'Eligible non-SRI svg script was correctly blocked by the CSP.');
+ break;
+ case 'bad_worker_could_load':
+ ok(false, 'require-sri-for failed to block loading a Worker with no integrity metadata.');
+ break;
+ case 'good_worker_could_load':
+ ok(true, "Loaded a worker that has require-sri-for set (but its parent doesnt).")
+ break;
+ case 'bad_worker_could_load_via_importScripts':
+ ok(false, 'require-sri-for failed to block loading importScript in a worker though we require SRI via CSP');
+ break;
+ case 'good_worker_after_importscripts':
+ ok(true, 'Worker continued after failed importScript due to require-sri-for');
+ break;
+ case 'finish':
+ finished++;
+ if (finished > 1) {
+ // need finish message from iframe_require-sri-for_main onload event and
+ // from iframe_require-sri-for_no_csp, which spawns a Worker
+ var blackText = frame.contentDocument.getElementById('black-text');
+ var blackTextColor = frame.contentWindow.getComputedStyle(blackText, null).getPropertyValue('color');
+ ok(blackTextColor == 'rgb(0, 0, 0)', "The second part should not be black.");
+ removeEventListener('message', handler);
+ SimpleTest.finish();
+ }
+ break;
+ default:
+ ok(false, 'Something is wrong here');
+ break;
+ }
+ }
+ addEventListener("message", handler);
+ // This frame has a CSP that requires SRI
+ var frame = document.getElementById("test_frame");
+ frame.src = "iframe_require-sri-for_main.html";
+ // This frame has no CSP to require SRI.
+ // Used for testing require-sri-for in a Worker.
+ var frame_no_csp = document.getElementById("test_frame_no_csp");
+ frame_no_csp.src = "iframe_require-sri-for_no_csp.html";
+</script>
+</html>
diff --git a/dom/security/test/sri/test_require-sri-for_csp_directive_disabled.html b/dom/security/test/sri/test_require-sri-for_csp_directive_disabled.html
new file mode 100644
index 000000000..b833fe11c
--- /dev/null
+++ b/dom/security/test/sri/test_require-sri-for_csp_directive_disabled.html
@@ -0,0 +1,46 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for diabled SRI require-sri-for CSP directive</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265318">Mozilla Bug 1265318</a>
+<iframe style="width:200px;height:200px;" id="test_frame"></iframe>
+</body>
+<script type="application/javascript">
+ SpecialPowers.setBoolPref("security.csp.experimentalEnabled", false);
+ SimpleTest.waitForExplicitFinish();
+ function handler(event) {
+ switch (event.data) {
+ case 'good_sriLoaded':
+ ok(true, "Eligible SRI resources was correctly loaded.");
+ break;
+ case 'bad_nonsriLoaded':
+ ok(true, "Eligible non-SRI resource should be blocked by the CSP!");
+ break;
+ case 'good_nonsriBlocked':
+ ok(false, "Eligible non-SRI resources was correctly blocked by the CSP.");
+ break;
+ case 'finish':
+ var blackText = frame.contentDocument.getElementById('black-text');
+ var blackTextColor = frame.contentWindow.getComputedStyle(blackText, null).getPropertyValue('color');
+ ok(blackTextColor != 'rgb(0, 0, 0)', "The second part should still be black.");
+ removeEventListener('message', handler);
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, 'Something is wrong here');
+ break;
+ }
+ }
+ addEventListener("message", handler);
+ var frame = document.getElementById("test_frame");
+ frame.src = "iframe_require-sri-for_main.html";
+</script>
+</html>
diff --git a/dom/security/test/sri/test_script_crossdomain.html b/dom/security/test/sri/test_script_crossdomain.html
new file mode 100644
index 000000000..327ea4247
--- /dev/null
+++ b/dom/security/test/sri/test_script_crossdomain.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Cross-domain script tests for Bug 992096</title>
+ <script>
+ SpecialPowers.setBoolPref("security.sri.enable", true);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=992096">Mozilla Bug 992096</a>
+<div>
+ <iframe src="iframe_script_crossdomain.html" height="100%" width="90%" frameborder="0"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/security/test/sri/test_script_sameorigin.html b/dom/security/test/sri/test_script_sameorigin.html
new file mode 100644
index 000000000..df890d5f9
--- /dev/null
+++ b/dom/security/test/sri/test_script_sameorigin.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Same-origin script tests for Bug 992096</title>
+ <script>
+ SpecialPowers.setBoolPref("security.sri.enable", true);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=992096">Mozilla Bug 992096</a>
+<div>
+ <iframe src="iframe_script_sameorigin.html" height="100%" width="90%" frameborder="0"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/security/test/sri/test_sri_disabled.html b/dom/security/test/sri/test_sri_disabled.html
new file mode 100644
index 000000000..4661235b1
--- /dev/null
+++ b/dom/security/test/sri/test_sri_disabled.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>security.sri.enable tests for Bug 992096</title>
+ <script>
+ SpecialPowers.setBoolPref("security.sri.enable", false);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=992096">Mozilla Bug 992096</a>
+<div>
+ <iframe src="iframe_sri_disabled.html" height="100%" width="90%" frameborder="0"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/security/test/sri/test_style_crossdomain.html b/dom/security/test/sri/test_style_crossdomain.html
new file mode 100644
index 000000000..6bf4b2de1
--- /dev/null
+++ b/dom/security/test/sri/test_style_crossdomain.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Cross-domain stylesheet tests for Bug 1196740</title>
+ <script>
+ SpecialPowers.setBoolPref("security.sri.enable", true);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1196740">Mozilla Bug 1196740</a>
+<div>
+ <iframe src="iframe_style_crossdomain.html" height="100%" width="90%" frameborder="0"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/security/test/sri/test_style_sameorigin.html b/dom/security/test/sri/test_style_sameorigin.html
new file mode 100644
index 000000000..fb1d3f78d
--- /dev/null
+++ b/dom/security/test/sri/test_style_sameorigin.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Same-origin stylesheet tests for Bug 992096</title>
+ <script>
+ SpecialPowers.setBoolPref("security.sri.enable", true);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=992096">Mozilla Bug 992096</a>
+<div>
+ <iframe src="iframe_style_sameorigin.html" height="100%" width="90%" frameborder="0"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/security/test/unit/test_csp_reports.js b/dom/security/test/unit/test_csp_reports.js
new file mode 100644
index 000000000..6c88fb1e1
--- /dev/null
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -0,0 +1,231 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import('resource://gre/modules/NetUtil.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+var httpServer = new HttpServer();
+httpServer.start(-1);
+var testsToFinish = 0;
+
+var principal;
+
+const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
+const REPORT_SERVER_URI = "http://localhost";
+const REPORT_SERVER_PATH = "/report";
+
+/**
+ * Construct a callback that listens to a report submission and either passes
+ * or fails a test based on what it gets.
+ */
+function makeReportHandler(testpath, message, expectedJSON) {
+ return function(request, response) {
+ // we only like "POST" submissions for reports!
+ if (request.method !== "POST") {
+ do_throw("violation report should be a POST request");
+ return;
+ }
+
+ // check content-type of report is "application/csp-report"
+ var contentType = request.hasHeader("Content-Type")
+ ? request.getHeader("Content-Type") : undefined;
+ if (contentType !== "application/csp-report") {
+ do_throw("violation report should have the 'application/csp-report' " +
+ "content-type, when in fact it is " + contentType.toString())
+ }
+
+ // obtain violation report
+ var reportObj = JSON.parse(
+ NetUtil.readInputStreamToString(
+ request.bodyInputStream,
+ request.bodyInputStream.available()));
+
+ // dump("GOT REPORT:\n" + JSON.stringify(reportObj) + "\n");
+ // dump("TESTPATH: " + testpath + "\n");
+ // dump("EXPECTED: \n" + JSON.stringify(expectedJSON) + "\n\n");
+
+ for (var i in expectedJSON)
+ do_check_eq(expectedJSON[i], reportObj['csp-report'][i]);
+
+ testsToFinish--;
+ httpServer.registerPathHandler(testpath, null);
+ if (testsToFinish < 1)
+ httpServer.stop(do_test_finished);
+ else
+ do_test_finished();
+ };
+}
+
+/**
+ * Everything created by this assumes it will cause a report. If you want to
+ * add a test here that will *not* cause a report to go out, you're gonna have
+ * to make sure the test cleans up after itself.
+ */
+function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
+ testsToFinish++;
+ do_test_pending();
+
+ // set up a new CSP instance for each test.
+ var csp = Cc["@mozilla.org/cspcontext;1"]
+ .createInstance(Ci.nsIContentSecurityPolicy);
+ var policy = "default-src 'none'; " +
+ "report-uri " + REPORT_SERVER_URI +
+ ":" + REPORT_SERVER_PORT +
+ "/test" + id;
+ var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
+ ":" + REPORT_SERVER_PORT +
+ "/foo/self");
+
+ dump("Created test " + id + " : " + policy + "\n\n");
+
+ let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager);
+ principal = ssm.createCodebasePrincipal(selfuri, {});
+ csp.setRequestContext(null, principal);
+
+ // Load up the policy
+ // set as report-only if that's the case
+ csp.appendPolicy(policy, useReportOnlyPolicy, false);
+
+ // prime the report server
+ var handler = makeReportHandler("/test" + id, "Test " + id, expectedJSON);
+ httpServer.registerPathHandler("/test" + id, handler);
+
+ //trigger the violation
+ callback(csp);
+}
+
+function run_test() {
+ var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
+ ":" + REPORT_SERVER_PORT +
+ "/foo/self");
+
+ // test that inline script violations cause a report.
+ makeTest(0, {"blocked-uri": "self"}, false,
+ function(csp) {
+ let inlineOK = true;
+ inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
+ "", // aNonce
+ false, // aParserCreated
+ "", // aContent
+ 0); // aLineNumber
+
+ // this is not a report only policy, so it better block inline scripts
+ do_check_false(inlineOK);
+ });
+
+ // test that eval violations cause a report.
+ makeTest(1, {"blocked-uri": "self",
+ // JSON script-sample is UTF8 encoded
+ "script-sample" : "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9"}, false,
+ function(csp) {
+ let evalOK = true, oReportViolation = {'value': false};
+ evalOK = csp.getAllowsEval(oReportViolation);
+
+ // this is not a report only policy, so it better block eval
+ do_check_false(evalOK);
+ // ... and cause reports to go out
+ do_check_true(oReportViolation.value);
+
+ if (oReportViolation.value) {
+ // force the logging, since the getter doesn't.
+ csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL,
+ selfuri.asciiSpec,
+ // sending UTF-16 script sample to make sure
+ // csp report in JSON is not cut-off, please
+ // note that JSON is UTF8 encoded.
+ "\u00a3\u00a5\u00b5\u5317\ud841\udf79",
+ 1);
+ }
+ });
+
+ makeTest(2, {"blocked-uri": "http://blocked.test"}, false,
+ function(csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
+ NetUtil.newURI("http://blocked.test/foo.js"),
+ null, null, null, null);
+ });
+
+ // test that inline script violations cause a report in report-only policy
+ makeTest(3, {"blocked-uri": "self"}, true,
+ function(csp) {
+ let inlineOK = true;
+ inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
+ "", // aNonce
+ false, // aParserCreated
+ "", // aContent
+ 0); // aLineNumber
+
+ // this is a report only policy, so it better allow inline scripts
+ do_check_true(inlineOK);
+ });
+
+ // test that eval violations cause a report in report-only policy
+ makeTest(4, {"blocked-uri": "self"}, true,
+ function(csp) {
+ let evalOK = true, oReportViolation = {'value': false};
+ evalOK = csp.getAllowsEval(oReportViolation);
+
+ // this is a report only policy, so it better allow eval
+ do_check_true(evalOK);
+ // ... but still cause reports to go out
+ do_check_true(oReportViolation.value);
+
+ if (oReportViolation.value) {
+ // force the logging, since the getter doesn't.
+ csp.logViolationDetails(Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT,
+ selfuri.asciiSpec,
+ "script sample",
+ 4);
+ }
+ });
+
+ // test that only the uri's scheme is reported for globally unique identifiers
+ makeTest(5, {"blocked-uri": "data"}, false,
+ function(csp) {
+ var base64data =
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
+ NetUtil.newURI("data:image/png;base64," + base64data),
+ null, null, null, null);
+ });
+
+ // test that only the uri's scheme is reported for globally unique identifiers
+ makeTest(6, {"blocked-uri": "intent"}, false,
+ function(csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
+ NetUtil.newURI("intent://mymaps.com/maps?um=1&ie=UTF-8&fb=1&sll"),
+ null, null, null, null);
+ });
+
+ // test fragment removal
+ var selfSpec = REPORT_SERVER_URI + ":" + REPORT_SERVER_PORT + "/foo/self/foo.js";
+ makeTest(7, {"blocked-uri": selfSpec}, false,
+ function(csp) {
+ var uri = NetUtil
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
+ NetUtil.newURI(selfSpec + "#bar"),
+ null, null, null, null);
+ });
+
+ // test scheme of ftp:
+ makeTest(8, {"blocked-uri": "ftp://blocked.test"}, false,
+ function(csp) {
+ // shouldLoad creates and sends out the report here.
+ csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
+ NetUtil.newURI("ftp://blocked.test/profile.png"),
+ null, null, null, null);
+ });
+}
diff --git a/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js
new file mode 100644
index 000000000..8be873234
--- /dev/null
+++ b/dom/security/test/unit/test_csp_upgrade_insecure_request_header.js
@@ -0,0 +1,107 @@
+var Cu = Components.utils;
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+
+// Since this test creates a TYPE_DOCUMENT channel via javascript, it will
+// end up using the wrong LoadInfo constructor. Setting this pref will disable
+// the ContentPolicyType assertion in the constructor.
+prefs.setBoolPref("network.loadinfo.skip_type_assertion", true);
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+ return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+var httpserver = null;
+var channel = null;
+var curTest = null;
+var testpath = "/footpath";
+
+var tests = [
+ {
+ description: "should not set request header for TYPE_OTHER",
+ expectingHeader: false,
+ contentType: Ci.nsIContentPolicy.TYPE_OTHER
+ },
+ {
+ description: "should set request header for TYPE_DOCUMENT",
+ expectingHeader: true,
+ contentType: Ci.nsIContentPolicy.TYPE_DOCUMENT
+ },
+ {
+ description: "should set request header for TYPE_SUBDOCUMENT",
+ expectingHeader: true,
+ contentType: Ci.nsIContentPolicy.TYPE_SUBDOCUMENT
+ },
+ {
+ description: "should not set request header for TYPE_IMG",
+ expectingHeader: false,
+ contentType: Ci.nsIContentPolicy.TYPE_IMG
+ },
+];
+
+function ChannelListener() {
+}
+
+ChannelListener.prototype = {
+ onStartRequest: function(request, context) { },
+ onDataAvailable: function(request, context, stream, offset, count) {
+ do_throw("Should not get any data!");
+ },
+ onStopRequest: function(request, context, status) {
+ var upgrade_insecure_header = false;
+ try {
+ if (request.getRequestHeader("Upgrade-Insecure-Requests")) {
+ upgrade_insecure_header = true;
+ }
+ }
+ catch (e) {
+ // exception is thrown if header is not available on the request
+ }
+ // debug
+ // dump("executing test: " + curTest.description);
+ do_check_eq(upgrade_insecure_header, curTest.expectingHeader)
+ run_next_test();
+ },
+};
+
+function setupChannel(aContentType) {
+ var chan = NetUtil.newChannel({
+ uri: URL + testpath,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: aContentType
+ });
+ chan.QueryInterface(Ci.nsIHttpChannel);
+ chan.requestMethod = "GET";
+ return chan;
+}
+
+function serverHandler(metadata, response) {
+ // no need to perform anything here
+}
+
+function run_next_test() {
+ curTest = tests.shift();
+ if (!curTest) {
+ httpserver.stop(do_test_finished);
+ return;
+ }
+ channel = setupChannel(curTest.contentType);
+ channel.asyncOpen2(new ChannelListener());
+}
+
+function run_test() {
+ // set up the test environment
+ httpserver = new HttpServer();
+ httpserver.registerPathHandler(testpath, serverHandler);
+ httpserver.start(-1);
+
+ run_next_test();
+ do_test_pending();
+}
diff --git a/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
new file mode 100644
index 000000000..7de8faa8f
--- /dev/null
+++ b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests the "Is origin potentially trustworthy?" logic from
+ * <https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy>.
+ */
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gScriptSecurityManager",
+ "@mozilla.org/scriptsecuritymanager;1",
+ "nsIScriptSecurityManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
+ "@mozilla.org/contentsecuritymanager;1",
+ "nsIContentSecurityManager");
+
+var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+prefs.setCharPref("dom.securecontext.whitelist", "example.net,example.org");
+
+add_task(function* test_isOriginPotentiallyTrustworthy() {
+ for (let [uriSpec, expectedResult] of [
+ ["http://example.com/", false],
+ ["https://example.com/", true],
+ ["http://localhost/", true],
+ ["http://127.0.0.1/", true],
+ ["file:///", true],
+ ["resource:///", true],
+ ["app://", true],
+ ["moz-extension://", true],
+ ["wss://example.com/", true],
+ ["about:config", false],
+ ["urn:generic", false],
+ ["http://example.net/", true],
+ ["ws://example.org/", true],
+ ["chrome://example.net/content/messenger.xul", false],
+ ]) {
+ let uri = NetUtil.newURI(uriSpec);
+ let principal = gScriptSecurityManager.getCodebasePrincipal(uri);
+ Assert.equal(gContentSecurityManager.isOriginPotentiallyTrustworthy(principal),
+ expectedResult);
+ }
+});
diff --git a/dom/security/test/unit/xpcshell.ini b/dom/security/test/unit/xpcshell.ini
new file mode 100644
index 000000000..7e1d4a0ed
--- /dev/null
+++ b/dom/security/test/unit/xpcshell.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+head =
+tail =
+
+[test_csp_reports.js]
+[test_isOriginPotentiallyTrustworthy.js]
+[test_csp_upgrade_insecure_request_header.js]