diff --git a/README.md b/README.md
index 6c61279..2cd1f3c 100644
--- a/README.md
+++ b/README.md
@@ -53,10 +53,10 @@
## Features
-:heavy_check_mark: 55 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.
+:heavy_check_mark: 56 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.
:heavy_check_mark: 14 [checks](#checks-missing-headers) for missing security-related HTTP response headers (the ones I consider essential).
:heavy_check_mark: 1186 [checks](#checks-fingerprint-headers) for fingerprinting through HTTP response headers.
-:heavy_check_mark: 121 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.
+:heavy_check_mark: 122 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.
:heavy_check_mark: SSL/TLS checks: requires the **amazing** https://testssl.sh/.
:heavy_check_mark: Browser support references for enabled HTTP security headers: provided by https://caniuse.com/.
:heavy_check_mark: Two types of analysis: brief and detailed, along with HTTP response headers.
diff --git a/additional/insecure.txt b/additional/insecure.txt
index fb347aa..f4c6d27 100644
--- a/additional/insecure.txt
+++ b/additional/insecure.txt
@@ -63,6 +63,7 @@ Cross-Origin-Embedder-Policy: No Valid Directives
Cross-Origin-Opener-Policy: No Valid Directives
Cross-Origin-Resource-Policy: No Valid Directives
Digest: Deprecated Header
+Document-Isolation-Policy: No Valid Directives
Document-Policy: No Valid Directives
Etag: Potentially Unsafe Header
Expect-CT: Deprecated Header
diff --git a/additional/security.txt b/additional/security.txt
index cbb48ad..8370e3e 100644
--- a/additional/security.txt
+++ b/additional/security.txt
@@ -45,6 +45,7 @@ Cross-Origin-Embedder-Policy-Report-Only
Cross-Origin-Opener-Policy
Cross-Origin-Opener-Policy-Report-Only
Cross-Origin-Resource-Policy
+Document-Isolation-Policy
Document-Policy
Expect-CT
Feature-Policy
diff --git a/humble.py b/humble.py
index d573b70..59fd6df 100644
--- a/humble.py
+++ b/humble.py
@@ -1923,6 +1923,9 @@ def custom_help_formatter(prog):
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy
t_corp = ('cross-origin', 'same-origin', 'same-site')
+# https://wicg.github.io/document-isolation-policy/
+t_doci = ('isolate-and-credentialless', 'isolate-and-require-corp', 'none')
+
# https://wicg.github.io/document-policy/
# https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md
t_docp = ('bpp', 'document-write', 'frame-loading', 'forms',
@@ -2221,23 +2224,28 @@ def custom_help_formatter(prog):
if 'digest' in headers_l and '23' not in skip_list:
print_details('[idig_h]', '[idig]', 'd', i_cnt)
-if 'document-policy' in headers_l and '24' not in skip_list:
+if 'document-isolation-policy' in headers_l and '24' not in skip_list:
+ doci_h = headers_l['document-isolation-policy']
+ if not any(elem in doci_h for elem in t_doci):
+ print_details('[idocpi_h]', '[idocpi]', 'd', i_cnt)
+
+if 'document-policy' in headers_l and '25' not in skip_list:
docp_h = headers_l['document-policy']
if not any(elem in docp_h for elem in t_docp):
print_details('[idocp_h]', '[idoc]', 'm', i_cnt)
-if 'etag' in headers_l and '25' not in skip_list:
+if 'etag' in headers_l and '26' not in skip_list:
print_details('[ieta_h]', '[ieta]', 'd', i_cnt)
-if 'expect-ct' in headers_l and '26' not in skip_list:
+if 'expect-ct' in headers_l and '27' not in skip_list:
print_details('[iexct_h]', '[iexct]', 'm', i_cnt)
if 'expires' in headers_l and any(elem in headers_l.get('cache-control', '')
- for elem in t_excc) and '27' \
+ for elem in t_excc) and '28' \
not in skip_list:
print_details('[iexpi_h]', '[iexpi]', 'd', i_cnt)
-if 'feature-policy' in headers_l and '28' not in skip_list:
+if 'feature-policy' in headers_l and '29' not in skip_list:
print_details('[iffea_h]', '[iffea]', 'd', i_cnt)
if unsafe_scheme:
@@ -2245,41 +2253,41 @@ def custom_help_formatter(prog):
if ('keep-alive' in headers_l and headers_l['keep-alive'] and
('connection' not in headers_l or
- headers_l['connection'] != 'keep-alive')) and '30' not in skip_list:
+ headers_l['connection'] != 'keep-alive')) and '31' not in skip_list:
print_details('[ickeep_h]', '[ickeep]', 'd', i_cnt)
-if 'large-allocation' in headers_l and '31' not in skip_list:
+if 'large-allocation' in headers_l and '32' not in skip_list:
print_details('[ixlalloc_h]', '[ixallocd]', 'd', i_cnt)
-if 'nel' in headers_l and '32' not in skip_list:
+if 'nel' in headers_l and '33' not in skip_list:
nel_header = headers_l['nel']
if not any(elem in nel_header for elem in t_nel_dir):
print_details('[inel_h]', '[inel]', 'd', i_cnt)
if not all(elem in nel_header for elem in t_nel_req):
print_details('[inelm_h]', '[inelm]', "d", i_cnt)
-if 'no-vary-search' in headers_l and '33' not in skip_list:
+if 'no-vary-search' in headers_l and '34' not in skip_list:
nvarys_header = headers_l['no_vary-search']
if not any(elem in nvarys_header for elem in t_nvarysearch):
print_details('[ifnvarys_h]', '[ifnvarys]', 'd', i_cnt)
observe_brows_header = headers_l.get('observe-browsing-topics', '')
if observe_brows_header and '?1' not in observe_brows_header and \
- '34' not in skip_list:
+ '35' not in skip_list:
print_details('[iobsb_h]', '[iobsb]', 'd', i_cnt)
-if 'onion-location' in headers_l and '35' not in skip_list:
+if 'onion-location' in headers_l and '36' not in skip_list:
print_details('[ionloc_h]', '[ionloc]', 'm', i_cnt)
-if 'origin-agent-cluster' in headers_l and '36' not in skip_list:
+if 'origin-agent-cluster' in headers_l and '37' not in skip_list:
origin_cluster_h = headers_l['origin-agent-cluster']
if not any(elem in origin_cluster_h for elem in l_origcluster):
print_details('[iorigcluster_h]', '[iorigcluster]', 'd', i_cnt)
-if 'p3p' in headers_l and '37' not in skip_list:
+if 'p3p' in headers_l and '38' not in skip_list:
print_details('[ip3p_h]', '[ip3p]', 'd', i_cnt)
-if 'permissions-policy' in headers_l and '38' not in skip_list:
+if 'permissions-policy' in headers_l and '39' not in skip_list:
perm_header = headers_l['permissions-policy']
if not any(elem in perm_header for elem in t_per_ft):
print_details('[ifpoln_h]', '[ifpoln]', 'm', i_cnt)
@@ -2296,24 +2304,24 @@ def custom_help_formatter(prog):
print_detail('[ifpold]')
i_cnt[0] += 1
-if 'pragma' in headers_l and '39' not in skip_list:
+if 'pragma' in headers_l and '40' not in skip_list:
print_details('[iprag_h]', '[iprag]', 'd', i_cnt)
-if 'proxy-authenticate' in headers_l and '40' not in skip_list:
+if 'proxy-authenticate' in headers_l and '41' not in skip_list:
prxyauth_h = headers_l['proxy-authenticate']
if 'basic' in prxyauth_h and unsafe_scheme:
print_details('[iprxauth_h]', '[ihbas]', 'd', i_cnt)
if not any(elem in prxyauth_h for elem in t_proxy_auth):
print_details('[iprxauthn_h]', '[iprxauthn]', 'd', i_cnt)
-if 'public-key-pins' in headers_l and '41' not in skip_list:
+if 'public-key-pins' in headers_l and '42' not in skip_list:
print_details('[ipkp_h]', '[ipkp]', 'd', i_cnt)
-if 'public-key-pins-report-only' in headers_l and '42' not in skip_list:
+if 'public-key-pins-report-only' in headers_l and '43' not in skip_list:
print_details('[ipkpr_h]', '[ipkp]', 'd', i_cnt)
referrer_header = headers_l.get('referrer-policy', '')
-if referrer_header and '43' not in skip_list:
+if referrer_header and '44' not in skip_list:
if not any(elem in referrer_header for elem in t_ref_secure):
print_details('[iref_h]', '[iref]', 'm', i_cnt)
if 'unsafe-url' in referrer_header:
@@ -2322,29 +2330,29 @@ def custom_help_formatter(prog):
print_details('[irefn_h]', '[irefn]', 'd', i_cnt)
refresh_header = headers_l.get('refresh', '')
-if refresh_header and '44' not in skip_list and \
+if refresh_header and '45' not in skip_list and \
any(elem in refresh_header for elem in t_refresh):
print_details('[irefr_h]', '[irefr]', 'd', i_cnt)
-if 'report-to' in headers_l and '45' not in skip_list:
+if 'report-to' in headers_l and '46' not in skip_list:
print_details('[irept_h]', '[irept]', 'd', i_cnt)
report_h = headers_l.get('reporting-endpoints', '')
-if report_h and '46' not in skip_list and HTTP_SCHEMES[0] in report_h:
+if report_h and '47' not in skip_list and HTTP_SCHEMES[0] in report_h:
print_details('[irepe_h]', '[irepe]', 'd', i_cnt)
repdig_header = headers_l.get('repr-digest', '')
-if repdig_header and '47' not in skip_list:
+if repdig_header and '48' not in skip_list:
if not any(elem in repdig_header for elem in t_repdig_sec):
print_details('[irepdig_h]', '[irepdig]', 'd', i_cnt)
if any(elem in repdig_header for elem in t_repdig_ins):
print_details('[irepdigi_h]', '[irepdigi]', 'm', i_cnt)
-if 'server-timing' in headers_l and '48' not in skip_list:
+if 'server-timing' in headers_l and '49' not in skip_list:
print_details('[itim_h]', '[itim]', 'd', i_cnt)
stc_header = headers_l.get("set-cookie", '')
-if stc_header and '49' not in skip_list:
+if stc_header and '50' not in skip_list:
if not unsafe_scheme and not all(elem in stc_header for elem in
t_cookie_sec):
print_details('[iset_h]', '[iset]', "d", i_cnt)
@@ -2358,20 +2366,20 @@ def custom_help_formatter(prog):
setlogin_header = headers_l.get("set-login", '')
if setlogin_header and not any(elem in setlogin_header for elem in t_setlogin)\
- and '50' not in skip_list:
+ and '51' not in skip_list:
print_details('[islogin_h]', '[islogin]', 'd', i_cnt)
-if 'sourcemap' in headers_l and '51' not in skip_list:
+if 'sourcemap' in headers_l and '52' not in skip_list:
print_details('[ismap_m]', '[ismap]', 'd', i_cnt)
-if 'speculation-rules' in headers_l and '52' not in skip_list:
+if 'speculation-rules' in headers_l and '53' not in skip_list:
print_details('[ispec_m]', '[ispec]', 'm', i_cnt)
-if 'strict-dynamic' in headers_l and '53' not in skip_list:
+if 'strict-dynamic' in headers_l and '54' not in skip_list:
print_details('[isdyn_h]', '[isdyn]', 'd', i_cnt)
sts_header = headers_l.get('strict-transport-security', '')
-if sts_header and '54' not in skip_list:
+if sts_header and '55' not in skip_list:
try:
age = int(''.join(filter(str.isdigit, sts_header)))
if unsafe_scheme:
@@ -2386,25 +2394,25 @@ def custom_help_formatter(prog):
except ValueError:
print_details('[ists_h]', '[ists]', 'm', i_cnt)
-if 'supports-loading-mode' in headers_l and '55' not in skip_list:
+if 'supports-loading-mode' in headers_l and '56' not in skip_list:
support_mode_h = headers_l['supports-loading-mode']
if unsafe_scheme:
print_details('[islmodei_h]', '[islmodei]', 'd', i_cnt)
if not any(elem in support_mode_h for elem in t_support_mode):
print_details('[islmode_h]', '[islmode]', 'd', i_cnt)
-if 'surrogate-control' in headers_l and '56' not in skip_list:
+if 'surrogate-control' in headers_l and '57' not in skip_list:
surrogate_mode_h = headers_l['surrogate-control']
if not any(elem in surrogate_mode_h for elem in t_surrogate):
print_details('[isurrmode_h]', '[isurrmode]', 'd', i_cnt)
-if headers_l.get('timing-allow-origin', '') == '*' and '57' not in skip_list:
+if headers_l.get('timing-allow-origin', '') == '*' and '58' not in skip_list:
print_details('[itao_h]', '[itao]', 'd', i_cnt)
-if 'tk' in headers_l and '58' not in skip_list:
+if 'tk' in headers_l and '59' not in skip_list:
print_details('[ixtk_h]', '[ixtkd]', 'd', i_cnt)
-if 'trailer' in headers_l and '59' not in skip_list:
+if 'trailer' in headers_l and '60' not in skip_list:
trailer_h = headers_l['trailer']
if any(elem in trailer_h for elem in t_trailer):
print_detail_r('[itrailer_h]', is_red=True)
@@ -2415,47 +2423,47 @@ def custom_help_formatter(prog):
print_detail('[itrailer_d_r]')
i_cnt[0] += 1
-if 'transfer-encoding' in headers_l and '60' not in skip_list:
+if 'transfer-encoding' in headers_l and '61' not in skip_list:
transfer_h = headers_l['transfer-encoding']
if not any(elem in transfer_h for elem in t_transfer):
print_details('[ictrf_h]', '[itrf]', 'd', i_cnt)
-if 'vary' in headers_l and '61' not in skip_list:
+if 'vary' in headers_l and '62' not in skip_list:
print_details('[ixvary_h]', '[ixvary]', 'm', i_cnt)
-if 'want-digest' in headers_l and '62' not in skip_list:
+if 'want-digest' in headers_l and '63' not in skip_list:
print_details('[ixwandig_h]', '[ixwandig]', 'd', i_cnt)
-if 'warning' in headers_l and '63' not in skip_list:
+if 'warning' in headers_l and '64' not in skip_list:
print_details('[ixwar_h]', '[ixward]', 'd', i_cnt)
wwwa_header = headers_l.get('www-authenticate', '')
-if wwwa_header and unsafe_scheme and ('basic' in wwwa_header) and '64' not in \
+if wwwa_header and unsafe_scheme and ('basic' in wwwa_header) and '65' not in \
skip_list:
print_details('[ihbas_h]', '[ihbas]', 'd', i_cnt)
-if 'x-content-security-policy' in headers_l and '65' not in skip_list:
+if 'x-content-security-policy' in headers_l and '66' not in skip_list:
print_details('[ixcsp_h]', '[ixcsp]', 'd', i_cnt)
-if 'x-content-security-policy-report-only' in headers_l and '66' not in \
+if 'x-content-security-policy-report-only' in headers_l and '67' not in \
skip_list:
print_details('[ixcspr_h]', '[ixcspr]', 'd', i_cnt)
-if 'x-content-type-options' in headers_l and '67' not in skip_list:
+if 'x-content-type-options' in headers_l and '68' not in skip_list:
if ',' in headers_l['x-content-type-options']:
print_details('[ictpd_h]', '[ictpd]', 'd', i_cnt)
elif 'nosniff' not in headers_l['x-content-type-options']:
print_details('[ictp_h]', '[ictp]', 'd', i_cnt)
-if headers_l.get('x-dns-prefetch-control', '') == 'on' and '68' not in \
+if headers_l.get('x-dns-prefetch-control', '') == 'on' and '69' not in \
skip_list:
print_details('[ixdp_h]', '[ixdp]', 'd', i_cnt)
-if 'x-download-options' in headers_l and '69' not in skip_list:
+if 'x-download-options' in headers_l and '70' not in skip_list:
print_details('[ixdow_h]', '[ixdow]', 'm', i_cnt)
xfo_header = headers_l.get('x-frame-options', '')
-if xfo_header and '70' not in skip_list:
+if xfo_header and '71' not in skip_list:
if ',' in xfo_header:
print_details('[ixfo_h]', '[ixfo]', 'm', i_cnt)
if 'allow-from' in xfo_header:
@@ -2463,11 +2471,11 @@ def custom_help_formatter(prog):
if xfo_header not in t_xfo_dir:
print_details('[ixfoi_h]', '[ixfodi]', 'm', i_cnt)
-if 'x-pad' in headers_l and '71' not in skip_list:
+if 'x-pad' in headers_l and '72' not in skip_list:
print_details('[ixpad_h]', '[ixpad]', 'd', i_cnt)
permcross_header = headers_l.get('x-permitted-cross-domain-policies', '')
-if permcross_header and '72' not in skip_list:
+if permcross_header and '73' not in skip_list:
if not any(elem in permcross_header for elem in t_permcross):
print_details('[ixpermcross_h]', '[ixpermcross]', 'm', i_cnt)
if 'all' in permcross_header:
@@ -2475,24 +2483,24 @@ def custom_help_formatter(prog):
if ',' in permcross_header:
print_details('[ixpermcrossd_h]', '[ixpermcrossd]', 'm', i_cnt)
-if headers_l.get('x-pingback', '').endswith('xmlrpc.php') and '73' not in \
+if headers_l.get('x-pingback', '').endswith('xmlrpc.php') and '74' not in \
skip_list:
print_details('[ixpb_h]', '[ixpb]', 'd', i_cnt)
robots_header = headers_l.get('x-robots-tag', '')
-if robots_header and '74' not in skip_list:
+if robots_header and '75' not in skip_list:
if not any(elem in robots_header for elem in t_robots):
print_details('[ixrobv_h]', '[ixrobv]', 'm', i_cnt)
if 'all' in robots_header:
print_details('[ixrob_h]', '[ixrob]', 'm', i_cnt)
-if 'x-runtime' in headers_l and '75' not in skip_list:
+if 'x-runtime' in headers_l and '76' not in skip_list:
print_details('[ixrun_h]', '[ixrun]', 'd', i_cnt)
-if 'x-sourcemap' in headers_l and '76' not in skip_list:
+if 'x-sourcemap' in headers_l and '77' not in skip_list:
print_details('[ixsrc_h]', '[ixsrc]', 'd', i_cnt)
-if 'x-ua-compatible' in headers_l and '77' not in skip_list:
+if 'x-ua-compatible' in headers_l and '78' not in skip_list:
print_details('[ixuacom_h]', '[ixuacom]', 'm', i_cnt)
if http_equiv:
@@ -2501,13 +2509,13 @@ def custom_help_formatter(prog):
in tuple):
print_details('[ixuameta_h]', '[ixuameta]', 'd', i_cnt)
-if 'x-webkit-csp' in headers_l and '78' not in skip_list:
+if 'x-webkit-csp' in headers_l and '79' not in skip_list:
print_details('[ixwcsp_h]', '[ixcsp]', 'd', i_cnt)
-if 'x-webkit-csp-report-only' in headers_l and '79' not in skip_list:
+if 'x-webkit-csp-report-only' in headers_l and '80' not in skip_list:
print_details('[ixwcspr_h]', '[ixcspr]', 'd', i_cnt)
-if 'x-xss-protection' in headers_l and '80' not in skip_list:
+if 'x-xss-protection' in headers_l and '81' not in skip_list:
print_details('[ixxpdp_h]', '[ixxpdp]', 'm', i_cnt)
if '0' not in headers_l['x-xss-protection']:
print_details('[ixxp_h]', '[ixxp]', 'm', i_cnt)
diff --git a/l10n/details.txt b/l10n/details.txt
index 5b195db..fe23808 100644
--- a/l10n/details.txt
+++ b/l10n/details.txt
@@ -300,6 +300,13 @@
Ref: https://wicg.github.io/document-policy/
Ref: https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md
+[idocpi_h]
+ Document-Isolation-Policy (No Valid Directives)
+
+[idocpi]
+ Include at least one valid directive.
+ Ref: https://wicg.github.io/document-isolation-policy/
+
[ixach_h]
Accept-CH (Ignored Header)
diff --git a/l10n/details_es.txt b/l10n/details_es.txt
index b6b89db..386555c 100644
--- a/l10n/details_es.txt
+++ b/l10n/details_es.txt
@@ -301,6 +301,13 @@
Ref: https://wicg.github.io/document-policy/
Ref: https://github.com/WICG/document-policy/blob/main/document-policy-explainer.md
+[idocpi_h]
+ Document-Isolation-Policy (Sin directivas adecuadas)
+
+[idocpi]
+ Incluya, al menos, una directiva correcta.
+ Ref: https://wicg.github.io/document-isolation-policy/
+
[ixachd_h]
Accept-CH (Valores obsoletos)