diff --git a/README.md b/README.md
index 13cb7b1..2700066 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
-
+
@@ -58,7 +58,7 @@
: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: 128 [checks](#checks-deprecated-headersprotocols-and-insecure-values) for deprecated HTTP response headers/protocols or with insecure/wrong values.
-:heavy_check_mark: Checks compliance with OWASP 'Secure Headers Project' Best Practices.
+:heavy_check_mark: Checks compliance with OWASP 'Secure Headers Project' Best Practices.
: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/humble.py b/humble.py
index e21f13b..bc3d8b3 100644
--- a/humble.py
+++ b/humble.py
@@ -133,7 +133,7 @@
URL_STRING = ('rfc-st', ' URL : ', 'caniuse')
current_time = datetime.now().strftime("%Y/%m/%d - %H:%M:%S")
-local_version = datetime.strptime('2024-12-20', '%Y-%m-%d').date()
+local_version = datetime.strptime('2024-12-21', '%Y-%m-%d').date()
class SSLContextAdapter(requests.adapters.HTTPAdapter):
@@ -1472,56 +1472,44 @@ def extract_compliance_headers(tmp_filename):
def extract_compliance_values(enabled_headers):
- check_cache = enabled_headers.get('cache-control', '')
- check_clear = enabled_headers.get('clear-site-data', '')
- check_csp = enabled_headers.get('content-security-policy', '')
- check_coep = enabled_headers.get('cross-origin-embedder-policy', '')
- check_coop = enabled_headers.get('cross-origin-opener-policy', '')
- check_corp = enabled_headers.get('cross-origin-resource-policy', '')
- check_perm = enabled_headers.get('(*) permissions-policy', '')
- check_ref = enabled_headers.get('referrer-policy', '')
- check_sts = enabled_headers.get('strict-transport-security', '')
- check_xcto = enabled_headers.get('x-content-type-options', '')
- check_xfo = enabled_headers.get('x-frame-options', '')
- check_xpcd = enabled_headers.get('x-permitted-cross-domain-policies', '')
- check_compliance_owasp(check_cache, check_clear, check_csp, check_coep,
- check_coop, check_corp, check_perm, check_ref,
- check_sts, check_xcto, check_xfo, check_xpcd)
-
-
-# Ashamed of this code... I promise to improve it!.
-def check_compliance_owasp(check_cache, check_clear, check_csp, check_coep,
- check_coop, check_corp, check_perm, check_ref,
- check_sts, check_xcto, check_xfo, check_xpcd):
- c_cnt = 0
- if not check_cache or any(elem not in check_cache for elem in t_ocache):
- c_cnt += 1
- if not check_clear or any(elem not in check_clear for elem in t_oclear):
- c_cnt += 1
- if not check_csp or any(elem not in check_csp for elem in t_ocsp):
- c_cnt += 1
- if not check_coep or any(elem not in check_coep for elem in t_ocoep):
- c_cnt += 1
- if not check_coop or any(elem not in check_coop for elem in t_ocoop):
- c_cnt += 1
- if not check_corp or any(elem not in check_corp for elem in t_ocorp):
- c_cnt += 1
- if not check_perm or any(elem not in check_perm for elem in t_operm):
- c_cnt += 1
- if not check_ref or any(elem not in check_ref for elem in t_oref):
- c_cnt += 1
- if not check_sts or any(elem not in check_sts for elem in t_osts):
- c_cnt += 1
- if not check_xcto or any(elem not in check_xcto for elem in t_oxcto):
- c_cnt += 1
- if not check_xfo or any(elem not in check_xfo for elem in t_oxfo):
- c_cnt += 1
- if not check_xpcd or any(elem not in check_xpcd for elem in t_oxpcd):
- c_cnt += 1
- if c_cnt > 0:
+ header_keys = ['cache-control', 'clear-site-data',
+ 'content-security-policy', 'cross-origin-embedder-policy',
+ 'cross-origin-opener-policy',
+ 'cross-origin-resource-policy', '(*) permissions-policy',
+ 'referrer-policy', 'strict-transport-security',
+ 'x-content-type-options', 'x-frame-options',
+ 'x-permitted-cross-domain-policies',]
+
+ header_val = [enabled_headers.get(key, '') for key in header_keys]
+ compliance_val = [t_ocache, t_oclear, t_ocsp, t_ocoep, t_ocoop, t_ocorp,
+ t_operm, t_oref, t_osts, t_oxcto, t_oxfo, t_oxpcd,]
+
+ check_compliance_owasp(header_keys, header_val, compliance_val)
+
+
+def check_compliance_owasp(header_keys, header_val, compliance_val):
+ non_cnt = 0
+ non_rules = []
+
+ for key, value, rules in zip(header_keys, header_val, compliance_val):
+ if not value or any(elem not in value for elem in rules):
+ non_cnt += 1
+ header_v = value or get_detail('[comp_header]')
+ non_rules.append(f"{STYLE[2]}{key.title()}{STYLE[5]}: {header_v}")
+
+ print_compliance_owasp(non_cnt, non_rules)
+
+
+def print_compliance_owasp(non_cnt, non_rules):
+ if non_cnt > 0:
print("")
- print_detail('[ko_owasp]', num_lines=2)
+ print_detail('[comp_ko_owasp]', num_lines=2)
+ for rule in non_rules:
+ print(f" {rule}")
print("")
+ print_detail('[comp_experimental]', 2)
+ else:
+ print_detail('[comp_ok_owasp]', num_lines=2)
def analyze_input_file(input_file):
diff --git a/l10n/details.txt b/l10n/details.txt
index ff30f51..7d17365 100644
--- a/l10n/details.txt
+++ b/l10n/details.txt
@@ -1642,6 +1642,17 @@ Unhandled exception type:
'(*)' meaning: Experimental HTTP response header
'(*)' ref: https://mdn.io/Experimental_deprecated_obsolete
-[ko_owasp]
- The HTTP response headers do *not* comply with OWASP 'Secure Headers Project' Best Practices.
- Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
\ No newline at end of file
+[comp_ko_owasp]
+ The following HTTP response headers do not comply with the OWASP 'Secure Headers Project' best practices.
+ Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
+
+[comp_ok_owasp]
+ The HTTP response headers comply with the OWASP 'Secure Headers Project' best practices.
+ Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
+
+[comp_experimental]
+ '(*)' meaning: Experimental HTTP response header
+ '(*)' ref: https://mdn.io/Experimental_deprecated_obsolete
+
+ [comp_header]
+(Header not enabled)
\ No newline at end of file
diff --git a/l10n/details_es.txt b/l10n/details_es.txt
index 2f79707..3487e75 100644
--- a/l10n/details_es.txt
+++ b/l10n/details_es.txt
@@ -1630,8 +1630,19 @@ Excepción no gestionada:
[experimental_header]
Significado de '(*)': Cabecera de respuesta HTTP experimental
- '(*)' ref: https://mdn.io/Experimental_deprecated_obsolete
+ Referencia de '(*)': https://mdn.io/Experimental_deprecated_obsolete
-[ko_owasp]
- Las cabeceras HTTP de respuesta *no* cumplen con las buenas prácticas de OWASP 'Secure Headers Project'.
- Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
\ No newline at end of file
+[comp_ko_owasp]
+ Las siguientes cabeceras de respuesta HTTP no cumplen con las mejores prácticas de OWASP 'Secure Headers Project'.
+ Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
+
+[comp_ok_owasp]
+ Las cabeceras de respuesta HTTP cumplen con las mejores prácticas de OWASP 'Secure Headers Project'.
+ Ref: https://owasp.org/www-project-secure-headers/#div-bestpractices
+
+[comp_experimental]
+ Significado de '(*)': Cabecera de respuesta HTTP experimental
+ Referencia de '(*)': https://mdn.io/Experimental_deprecated_obsolete
+
+[comp_header]
+(Cabecera no habilitada)
\ No newline at end of file