Skip to content

Commit

Permalink
Feature: 1 new insecure check
Browse files Browse the repository at this point in the history
Document-Isolation-Policy: No Valid Directives
  • Loading branch information
rfc-st committed Dec 6, 2024
1 parent 2b2b2c6 commit 3c8b6c1
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 58 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@

## Features

:heavy_check_mark: 55 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.<br />
:heavy_check_mark: 56 [checks](#checks-enabled-headers) for enabled security-related HTTP response headers.<br />
:heavy_check_mark: 14 [checks](#checks-missing-headers) for missing security-related HTTP response headers (the ones I consider essential).<br />
:heavy_check_mark: 1186 [checks](#checks-fingerprint-headers) for fingerprinting through HTTP response headers.<br />
:heavy_check_mark: 121 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.<br />
:heavy_check_mark: 122 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.<br />
:heavy_check_mark: SSL/TLS checks: requires the **amazing** https://testssl.sh/.<br />
:heavy_check_mark: Browser support references for enabled HTTP security headers: provided by https://caniuse.com/.<br />
:heavy_check_mark: Two types of analysis: brief and detailed, along with HTTP response headers.<br />
Expand Down
1 change: 1 addition & 0 deletions additional/insecure.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions additional/security.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
120 changes: 64 additions & 56 deletions humble.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -2221,65 +2224,70 @@ 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:
print_details('[ihttp_h]', '[ihttp]', 'd', i_cnt)

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)
Expand All @@ -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:
Expand All @@ -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)
Expand All @@ -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:
Expand All @@ -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)
Expand All @@ -2415,84 +2423,84 @@ 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:
print_details('[ixfod_h]', '[ixfod]', 'm', i_cnt)
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:
print_details('[ixpermcrossu_h]', '[ixpermcrossu]', 'm', i_cnt)
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:
Expand All @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions l10n/details.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
7 changes: 7 additions & 0 deletions l10n/details_es.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down

0 comments on commit 3c8b6c1

Please sign in to comment.