From ea6eb9477ee2369b7756444b82f4654042e02b04 Mon Sep 17 00:00:00 2001 From: Evan Lucchesi Leon <189633144+elucchesileon@users.noreply.github.com> Date: Fri, 23 May 2025 16:52:19 -0400 Subject: [PATCH 01/11] chore: ruff format --- custom_jinja_filters.py | 17 +++- modules/__init__.py | 4 +- modules/assets/__init__.py | 1 + modules/assets/assets.py | 23 +++-- modules/assets/assets_config.py | 8 +- modules/benefactors/__init__.py | 1 + modules/benefactors/benefactors.py | 4 +- modules/benefactors/benefactors_config.py | 5 +- modules/campaigns/__init__.py | 1 + modules/campaigns/campaigns.py | 5 +- modules/campaigns/campaigns_config.py | 6 +- modules/datasources/__init__.py | 24 +++-- modules/datasources/datasources.py | 35 ++++---- modules/datasources/datasources_config.py | 9 +- modules/groups/__init__.py | 4 +- modules/groups/groups_config.py | 11 +-- modules/matrices/matrices.py | 5 +- modules/matrices/matrices_config.py | 4 +- modules/mitigations/mitigations.py | 5 +- modules/mitigations/mitigations_config.py | 7 +- modules/random_page/random_page.py | 2 +- modules/resources/__init__.py | 9 +- modules/resources/resources.py | 58 ++++++++---- modules/resources/resources_config.py | 11 +-- modules/site_config.py | 17 +--- modules/software/__init__.py | 3 + modules/software/software_config.py | 12 +-- modules/tactics/__init__.py | 1 + modules/tactics/tactics.py | 6 +- modules/tactics/tactics_config.py | 11 +-- modules/techniques/techniques.py | 29 +++--- modules/techniques/techniques_config.py | 12 +-- modules/tests/tests.py | 2 +- modules/util/buildhelpers.py | 88 +++++++++++-------- modules/util/relationshipgetters.py | 2 + modules/util/relationshiphelpers.py | 2 + modules/versions/versions_config.py | 4 +- modules/website_build/website_build_config.py | 4 +- pelicanconf.py | 34 +++---- update-attack.py | 2 +- 40 files changed, 258 insertions(+), 230 deletions(-) diff --git a/custom_jinja_filters.py b/custom_jinja_filters.py index 654d694ff53..d93c61fc415 100644 --- a/custom_jinja_filters.py +++ b/custom_jinja_filters.py @@ -92,7 +92,7 @@ def get_html_citation(citations, citation_name): if citation: ref_number = None description = citation.get("description") - + if citation.get("number"): ref_number = citation["number"] else: @@ -101,10 +101,19 @@ def get_html_citation(citations, citation_name): citation["number"] = ref_number if not citation.get("url"): - reference_html = reference_marker_template_no_url.format(ref_number, ref_number, citation_name, description, ref_number) + reference_html = reference_marker_template_no_url.format( + ref_number, ref_number, citation_name, description, ref_number + ) else: reference_html = reference_marker_template.format( - ref_number, ref_number, description, citation_name, citation["url"], ref_number - 1, ref_number - 1, ref_number + ref_number, + ref_number, + description, + citation_name, + citation["url"], + ref_number - 1, + ref_number - 1, + ref_number, ) return reference_html @@ -158,7 +167,7 @@ def stixToHTML(data, citations, firstParagraphOnly, convert): citations (optional, object), if not None, add citation markers to the data. firstParagraphOnly (optional, boolean), if true, only return the first paragraph of the data in question. """ - if (convert): + if convert: # Replace data from markdown format data = markdown.markdown(data) diff --git a/modules/__init__.py b/modules/__init__.py index eda35e27112..e9636e7878a 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -14,7 +14,9 @@ def sort_menu_by_priority(): def sort_run_ptr_by_priority(): global run_ptr run_ptr = sorted(run_ptr, key=lambda k: k["priority"]) - print(f"Building website using the following modules in this order: {[pointer_info['module_name'] for pointer_info in run_ptr]}") + print( + f"Building website using the following modules in this order: {[pointer_info['module_name'] for pointer_info in run_ptr]}" + ) def check_redirections(redirections_list): diff --git a/modules/assets/__init__.py b/modules/assets/__init__.py index f39002b53e5..64f6ab8ef60 100644 --- a/modules/assets/__init__.py +++ b/modules/assets/__init__.py @@ -5,5 +5,6 @@ def get_priority(): return assets_config.priority + def run_module(): return (assets.generate_assets(), assets_config.module_name) diff --git a/modules/assets/assets.py b/modules/assets/assets.py index 25c166e201e..26f45360d10 100644 --- a/modules/assets/assets.py +++ b/modules/assets/assets.py @@ -51,18 +51,14 @@ def generate_markdown_files(): data = {} notes = util.relationshipgetters.get_objects_using_notes() - side_menu_data = util.buildhelpers.get_side_menu_data( - "Assets", "/assets/", asset_list_no_deprecated_revoked - ) + side_menu_data = util.buildhelpers.get_side_menu_data("Assets", "/assets/", asset_list_no_deprecated_revoked) data["side_menu_data"] = side_menu_data data["assets_table"] = get_assets_table_data(asset_list_no_deprecated_revoked) data["assets_list_len"] = str(len(asset_list_no_deprecated_revoked)) subs = assets_config.asset_index_md + json.dumps(data) - with open( - os.path.join(assets_config.asset_markdown_path, "overview.md"), "w", encoding="utf8" - ) as md_file: + with open(os.path.join(assets_config.asset_markdown_path, "overview.md"), "w", encoding="utf8") as md_file: md_file.write(subs) # Create the markdown for assets @@ -77,7 +73,8 @@ def generate_asset_md(asset, side_menu_data, notes): attack_id = util.buildhelpers.get_attack_id(asset) - if not attack_id: return + if not attack_id: + return data = {} data["attack_id"] = attack_id @@ -178,7 +175,8 @@ def get_assets_table_data(asset_list): assets_table_data = [] for asset in asset_list: attack_id = util.buildhelpers.get_attack_id(asset) - if not attack_id: continue + if not attack_id: + continue domain_list = util.buildhelpers.get_domain_name(asset) row = { @@ -187,7 +185,7 @@ def get_assets_table_data(asset_list): } for domain_idx in range(len(domain_list)): - domain_list[domain_idx] = domain_list[domain_idx].replace('-attack','') + domain_list[domain_idx] = domain_list[domain_idx].replace("-attack", "") if domain_list[domain_idx] == "ics": domain_list[domain_idx] = domain_list[domain_idx].upper() else: @@ -206,12 +204,13 @@ def get_assets_table_data(asset_list): def get_related_asset_data(related_assets): - if not related_assets: return [] + if not related_assets: + return [] related_asset_data = [] for related_asset in related_assets: row = { - "name": related_asset["name"], # required + "name": related_asset["name"], # required } if related_asset.get("related_asset_sectors"): related_asset["related_asset_sectors"].sort() @@ -224,7 +223,7 @@ def get_related_asset_data(related_assets): def get_techniques_targeting_asset_data(asset, reference_list): """Given an asset and its reference list, get the techniques targeting the asset. - Check the reference list for citations, if not found in list, add it. + Check the reference list for citations, if not found in list, add it. """ technique_list = {} techniques_targeting_assets = util.relationshipgetters.get_techniques_targeting_assets() diff --git a/modules/assets/assets_config.py b/modules/assets/assets_config.py index deafdb527d0..49c583c4a21 100644 --- a/modules/assets/assets_config.py +++ b/modules/assets/assets_config.py @@ -7,14 +7,10 @@ asset_markdown_path = "content/pages/assets/" # String template for asset index page -asset_index_md = ( - "Title: Asset overview\nTemplate: assets/assets-index\nsave_as: assets/index.html\ndata: " -) +asset_index_md = "Title: Asset overview\nTemplate: assets/assets-index\nsave_as: assets/index.html\ndata: " # String template for asset page -asset_md = Template( - "Title: ${name}\nTemplate: assets/asset\nsave_as: assets/${attack_id}/index.html\ndata: " -) +asset_md = Template("Title: ${name}\nTemplate: assets/asset\nsave_as: assets/${attack_id}/index.html\ndata: ") # Path for templates assets_templates_path = "modules/assets/templates/" diff --git a/modules/benefactors/__init__.py b/modules/benefactors/__init__.py index 68eb55f3e77..8fd0ea8cb93 100644 --- a/modules/benefactors/__init__.py +++ b/modules/benefactors/__init__.py @@ -17,5 +17,6 @@ def get_menu(): "children": [], } + def run_module(): return (benefactors.generate_benefactors(), benefactors_config.module_name) diff --git a/modules/benefactors/benefactors.py b/modules/benefactors/benefactors.py index 4861ab2fdbe..ac6045a0221 100644 --- a/modules/benefactors/benefactors.py +++ b/modules/benefactors/benefactors.py @@ -24,5 +24,7 @@ def generate_benefactors(): benefactors_md = benefactors_config.benefactors_md # write markdown to file - with open(os.path.join(benefactors_config.benefactors_markdown_path, "benefactors.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(benefactors_config.benefactors_markdown_path, "benefactors.md"), "w", encoding="utf8" + ) as md_file: md_file.write(benefactors_md) diff --git a/modules/benefactors/benefactors_config.py b/modules/benefactors/benefactors_config.py index 29ef1259880..fac102ce451 100644 --- a/modules/benefactors/benefactors_config.py +++ b/modules/benefactors/benefactors_config.py @@ -6,7 +6,10 @@ # String template for benefactors index page benefactors_md = ( - "Title: Benefactors\n" "Template: benefactors/benefactors\n" "save_as: resources/engage-with-attack/benefactors/index.html\n" "data: " + "Title: Benefactors\n" + "Template: benefactors/benefactors\n" + "save_as: resources/engage-with-attack/benefactors/index.html\n" + "data: " ) benefactors_templates_path = "modules/benefactors/templates" diff --git a/modules/campaigns/__init__.py b/modules/campaigns/__init__.py index 99ff4eaa234..cf52cc31ba6 100644 --- a/modules/campaigns/__init__.py +++ b/modules/campaigns/__init__.py @@ -5,5 +5,6 @@ def get_priority(): return campaigns_config.priority + def run_module(): return (campaigns.generate_campaigns(), campaigns_config.module_name) diff --git a/modules/campaigns/campaigns.py b/modules/campaigns/campaigns.py index eafbbadfcfd..4e61f930bf0 100644 --- a/modules/campaigns/campaigns.py +++ b/modules/campaigns/campaigns.py @@ -306,6 +306,7 @@ def get_software_table_data(campaign, reference_list): software_data = sorted(software_data, key=lambda k: k["name"].lower()) return software_data + def generate_sidebar_campaigns(side_menu_data): """Responsible for generating the sidebar for the campaigns pages.""" logger.info("Generating campaigns sidebar") @@ -316,5 +317,7 @@ def generate_sidebar_campaigns(side_menu_data): sidebar_campaigns_md = campaigns_config.sidebar_campaigns_md + json.dumps(data) # write markdown to file - with open(os.path.join(campaigns_config.campaign_markdown_path, "sidebar_campaigns.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(campaigns_config.campaign_markdown_path, "sidebar_campaigns.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_campaigns_md) diff --git a/modules/campaigns/campaigns_config.py b/modules/campaigns/campaigns_config.py index 3eae07cc697..5d908dd6400 100644 --- a/modules/campaigns/campaigns_config.py +++ b/modules/campaigns/campaigns_config.py @@ -8,12 +8,12 @@ # String template for campaign index page campaign_index_md = ( - "Title: Campaign overview\n" "Template: campaigns/campaigns-index\n" "save_as: campaigns/index.html\n" "data: " + "Title: Campaign overview\nTemplate: campaigns/campaigns-index\nsave_as: campaigns/index.html\ndata: " ) # String template for campaign page campaign_md = Template( - "Title: ${name}\n" "Template: campaigns/campaign\n" "save_as: campaigns/${attack_id}/index.html\n" "data: " + "Title: ${name}\nTemplate: campaigns/campaign\nsave_as: campaigns/${attack_id}/index.html\ndata: " ) # Path for templates @@ -26,4 +26,4 @@ "Template: general/sidebar-template \n" "save_as: campaigns/sidebar-campaigns/index.html\n" "data: " -) \ No newline at end of file +) diff --git a/modules/datasources/__init__.py b/modules/datasources/__init__.py index 56b61bcbdd9..8d40ac5f5ab 100644 --- a/modules/datasources/__init__.py +++ b/modules/datasources/__init__.py @@ -16,13 +16,23 @@ def get_menu(): "priority": datasources_config.priority, "children": [ {"display_name": "Data Sources", "url": "/datasources", "external_link": False, "children": []}, - {"display_name": "Mitigations", "url": "/mitigations/", "external_link": False, "children": [ - {"display_name": "Enterprise", "url": "/mitigations/enterprise/", "external_link": False, "children": []}, - {"display_name": "Mobile", "url": "/mitigations/mobile/", "external_link": False, "children": []}, - {"display_name": "ICS", "url": "/mitigations/ics/", "external_link": False, "children": []}, - ]}, - {"display_name": "Assets", "url": "/assets", "external_link": False, "children": [] } - ] + { + "display_name": "Mitigations", + "url": "/mitigations/", + "external_link": False, + "children": [ + { + "display_name": "Enterprise", + "url": "/mitigations/enterprise/", + "external_link": False, + "children": [], + }, + {"display_name": "Mobile", "url": "/mitigations/mobile/", "external_link": False, "children": []}, + {"display_name": "ICS", "url": "/mitigations/ics/", "external_link": False, "children": []}, + ], + }, + {"display_name": "Assets", "url": "/assets", "external_link": False, "children": []}, + ], } diff --git a/modules/datasources/datasources.py b/modules/datasources/datasources.py index 663a2a41f80..20f67914b74 100644 --- a/modules/datasources/datasources.py +++ b/modules/datasources/datasources.py @@ -229,7 +229,7 @@ def get_datasources_table_data(datasource_list): row["id"] = attack_id for domain_idx in range(len(domain_list)): - domain_list[domain_idx] = domain_list[domain_idx].replace('-attack','') + domain_list[domain_idx] = domain_list[domain_idx].replace("-attack", "") if domain_list[domain_idx] == "ics": domain_list[domain_idx] = domain_list[domain_idx].upper() else: @@ -322,21 +322,22 @@ def get_datacomponents_data(datasource, reference_list): def get_domains_of_datacomponent(datacomponent, techniques_detected_by_datacomponent, technique_to_domain): - """Retrives domains of given data component""" - domains_of_datacomponent = [] + """Retrives domains of given data component""" + domains_of_datacomponent = [] - # get data components to techniques mapping to find domains - techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"]) - if techniques_of_datacomp: - technique_list = {} - for technique_rel in techniques_of_datacomp: - attack_id = util.buildhelpers.get_attack_id(technique_rel["object"]) - if attack_id: - domain = technique_to_domain[attack_id].split("-")[0] - if not domain in domains_of_datacomponent: - domains_of_datacomponent.append(domain) + # get data components to techniques mapping to find domains + techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"]) + if techniques_of_datacomp: + technique_list = {} + for technique_rel in techniques_of_datacomp: + attack_id = util.buildhelpers.get_attack_id(technique_rel["object"]) + if attack_id: + domain = technique_to_domain[attack_id].split("-")[0] + if not domain in domains_of_datacomponent: + domains_of_datacomponent.append(domain) + + return domains_of_datacomponent - return domains_of_datacomponent def get_datasources_domain(datasource): """Responsible for generating the list of domains for the datasources and datacomponents.""" @@ -357,10 +358,12 @@ def get_datasources_domain(datasource): # get data component detections techniques_of_datacomp = techniques_detected_by_datacomponent.get(datacomponent["id"]) if techniques_of_datacomp: - domains_of_datacomponent = get_domains_of_datacomponent(datacomponent, techniques_detected_by_datacomponent, technique_to_domain) + domains_of_datacomponent = get_domains_of_datacomponent( + datacomponent, techniques_detected_by_datacomponent, technique_to_domain + ) # Add missing domains to data source for domain in domains_of_datacomponent: if not domain in domains_of_datasource: domains_of_datasource.append(domain) - return domains_of_datasource \ No newline at end of file + return domains_of_datasource diff --git a/modules/datasources/datasources_config.py b/modules/datasources/datasources_config.py index 584af6d1eff..3d062782e77 100644 --- a/modules/datasources/datasources_config.py +++ b/modules/datasources/datasources_config.py @@ -11,16 +11,13 @@ # String template for data sources index page datasource_index_md = ( - "Title: Data Sources overview\n" - "Template: datasources/datasources-index\n" - "save_as: datasources/index.html\n" - "data: " + "Title: Data Sources overview\nTemplate: datasources/datasources-index\nsave_as: datasources/index.html\ndata: " ) # String template for data source page datasource_md = Template( - "Title: ${name}\n" "Template: datasources/datasource\n" "save_as: datasources/${attack_id}/index.html\n" "data: " + "Title: ${name}\nTemplate: datasources/datasource\nsave_as: datasources/${attack_id}/index.html\ndata: " ) # Path for templates -datasources_templates_path = "modules/datasources/templates/" \ No newline at end of file +datasources_templates_path = "modules/datasources/templates/" diff --git a/modules/groups/__init__.py b/modules/groups/__init__.py index abe5567a490..608f6037250 100644 --- a/modules/groups/__init__.py +++ b/modules/groups/__init__.py @@ -6,6 +6,7 @@ def get_priority(): return groups_config.priority + def get_menu(): return { "display_name": groups_config.module_tab_name, @@ -17,8 +18,9 @@ def get_menu(): {"display_name": "Groups", "url": "/groups", "external_link": False, "children": []}, {"display_name": "Software", "url": "/software", "external_link": False, "children": []}, {"display_name": "Campaigns", "url": "/campaigns", "external_link": False, "children": []}, - ] + ], } + def run_module(): return (groups.generate_groups(), groups_config.module_name) diff --git a/modules/groups/groups_config.py b/modules/groups/groups_config.py index 20a7da88a4e..a096ebf0367 100644 --- a/modules/groups/groups_config.py +++ b/modules/groups/groups_config.py @@ -8,10 +8,10 @@ group_markdown_path = "content/pages/groups/" # String template for group index page -group_index_md = "Title: Group overview\n" "Template: groups/groups-index\n" "save_as: groups/index.html\n" "data: " +group_index_md = "Title: Group overview\nTemplate: groups/groups-index\nsave_as: groups/index.html\ndata: " # String template for group page -group_md = Template("Title: ${name}\n" "Template: groups/group\n" "save_as: groups/${attack_id}/index.html\n" "data: ") +group_md = Template("Title: ${name}\nTemplate: groups/group\nsave_as: groups/${attack_id}/index.html\ndata: ") # Path for templates groups_templates_path = "modules/groups/templates/" @@ -19,8 +19,5 @@ groups_redirection_location = "modules/groups/groups_redirections.json" sidebar_groups_md = ( - "Title: Groups Sidebar\n" - "Template: general/sidebar-template \n" - "save_as: groups/sidebar-groups/index.html\n" - "data: " -) \ No newline at end of file + "Title: Groups Sidebar\nTemplate: general/sidebar-template \nsave_as: groups/sidebar-groups/index.html\ndata: " +) diff --git a/modules/matrices/matrices.py b/modules/matrices/matrices.py index 22a3ab254bf..741f17671a7 100644 --- a/modules/matrices/matrices.py +++ b/modules/matrices/matrices.py @@ -260,6 +260,7 @@ def transform_tactic(tactic_id): return data, has_subtechniques, tour_technique + def generate_sidebar_matrices(side_menu_data): """Responsible for generating the sidebar for the matrices pages.""" logger.info("Generating matrices sidebar") @@ -270,5 +271,7 @@ def generate_sidebar_matrices(side_menu_data): sidebar_matrices_md = matrices_config.sidebar_matrices_md + json.dumps(data) # write markdown to file - with open(os.path.join(matrices_config.matrix_markdown_path, "sidebar_matrices.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(matrices_config.matrix_markdown_path, "sidebar_matrices.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_matrices_md) diff --git a/modules/matrices/matrices_config.py b/modules/matrices/matrices_config.py index be228ef91bd..0a0408519e4 100644 --- a/modules/matrices/matrices_config.py +++ b/modules/matrices/matrices_config.py @@ -18,9 +18,7 @@ ) # String template for main domain matrices -matrix_md = Template( - "Title: Matrix-${domain}\n" "Template: matrices/matrix\n" "save_as: matrices/${path}/index.html\n" "data: " -) +matrix_md = Template("Title: Matrix-${domain}\nTemplate: matrices/matrix\nsave_as: matrices/${path}/index.html\ndata: ") # String template for platform matrices platform_md = Template( diff --git a/modules/mitigations/mitigations.py b/modules/mitigations/mitigations.py index e5bab98ef11..f89e2ace629 100644 --- a/modules/mitigations/mitigations.py +++ b/modules/mitigations/mitigations.py @@ -229,6 +229,7 @@ def get_techniques_addressed_data(mitigation, reference_list): ) return technique_data + def generate_sidebar_mitigations(side_nav_data): """Responsible for generating the sidebar for the mitigations pages.""" logger.info("Generating mitigations sidebar") @@ -239,5 +240,7 @@ def generate_sidebar_mitigations(side_nav_data): sidebar_mitigations_md = mitigations_config.sidebar_mitigations_md + json.dumps(data) # write markdown to file - with open(os.path.join(mitigations_config.mitigation_markdown_path, "sidebar_mitigations.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(mitigations_config.mitigation_markdown_path, "sidebar_mitigations.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_mitigations_md) diff --git a/modules/mitigations/mitigations_config.py b/modules/mitigations/mitigations_config.py index f60ea69f4b4..1ba778f6bd4 100644 --- a/modules/mitigations/mitigations_config.py +++ b/modules/mitigations/mitigations_config.py @@ -27,10 +27,7 @@ # String template for all mitigations mitigation_md = Template( - "Title: ${name}-${domain}\n" - "Template: mitigations/mitigation\n" - "save_as: mitigations/${attack_id}/index.html\n" - "data: " + "Title: ${name}-${domain}\nTemplate: mitigations/mitigation\nsave_as: mitigations/${attack_id}/index.html\ndata: " ) sidebar_mitigations_md = ( @@ -38,4 +35,4 @@ "Template: general/sidebar-template \n" "save_as: mitigations/sidebar-mitigations/index.html\n" "data: " -) \ No newline at end of file +) diff --git a/modules/random_page/random_page.py b/modules/random_page/random_page.py index 604e7e65e17..09e73557439 100644 --- a/modules/random_page/random_page.py +++ b/modules/random_page/random_page.py @@ -16,7 +16,7 @@ def generate_json(): "groups": "Group", "software": "Software", "campaigns": "Campaign", - "assets": "Asset" + "assets": "Asset", } routes = {} diff --git a/modules/resources/__init__.py b/modules/resources/__init__.py index e1bd244cf5e..33477ec9925 100644 --- a/modules/resources/__init__.py +++ b/modules/resources/__init__.py @@ -27,12 +27,7 @@ def get_menu(): "external_link": False, "children": [], }, - { - "display_name": "ATT&CKcon", - "url": "/resources/attackcon/", - "external_link": False, - "children": [] - }, + {"display_name": "ATT&CKcon", "url": "/resources/attackcon/", "external_link": False, "children": []}, { "display_name": "ATT&CK Data & Tools", "url": "/resources/attack-data-and-tools/", @@ -63,7 +58,7 @@ def get_menu(): "url": "/resources/legal-and-branding/", "external_link": False, "children": [], - } + }, ], } diff --git a/modules/resources/resources.py b/modules/resources/resources.py index f79d78488c3..c287dbcbd97 100644 --- a/modules/resources/resources.py +++ b/modules/resources/resources.py @@ -15,6 +15,7 @@ import urllib.parse + def generate_resources(): """Responsible for generating the resources pages.""" logger.info("Generating Resources") @@ -62,6 +63,7 @@ def generate_resources(): generate_use_case_page() generate_sidebar_resources() + def copy_docs(module_docs_path): """Move module specific docs into the website's content directory for pelican.""" logger.info("Copying files to docs directory") @@ -79,17 +81,20 @@ def copy_docs(module_docs_path): else: shutil.copyfile(os.path.join(module_docs_path, doc), os.path.join(site_config.docs_dir, doc)) + def extract_video_id(url): - if '=' in url and '&' in url: - start = url.find('=') + 1 - end = url.find('&', start) + if "=" in url and "&" in url: + start = url.find("=") + 1 + end = url.find("&", start) return url[start:end] return url + def extract_playlist_id(url): parsed_url = urllib.parse.urlparse(url) query_params = urllib.parse.parse_qs(parsed_url.query) - return query_params.get('list', [None])[0] + return query_params.get("list", [None])[0] + def generate_training_pages(): """Responsible for generating the markdown pages of the training pages.""" @@ -104,13 +109,22 @@ def generate_training_pages(): if "lessons" in trainings[training_main][module]: if trainings[training_main][module]["is_youtube"]: # if videos are in a playlist - if "playlist" in trainings[training_main][module] and trainings[training_main][module]["playlist"] == True: - trainings[training_main][module]["first_video_id"] = extract_video_id(trainings[training_main][module]["lessons"][0]["youtube"]) - trainings[training_main][module]["playlist_id"] = extract_playlist_id(trainings[training_main][module]["lessons"][0]["youtube"]) + if ( + "playlist" in trainings[training_main][module] + and trainings[training_main][module]["playlist"] == True + ): + trainings[training_main][module]["first_video_id"] = extract_video_id( + trainings[training_main][module]["lessons"][0]["youtube"] + ) + trainings[training_main][module]["playlist_id"] = extract_playlist_id( + trainings[training_main][module]["lessons"][0]["youtube"] + ) # if videos are not in a playlist else: for lesson_idx in range(len(trainings[training_main][module]["lessons"])): - trainings[training_main][module]["lessons"][lesson_idx]["video_id"] = extract_video_id(trainings[training_main][module]["lessons"][lesson_idx]["youtube"]) + trainings[training_main][module]["lessons"][lesson_idx]["video_id"] = extract_video_id( + trainings[training_main][module]["lessons"][lesson_idx]["youtube"] + ) # Define a dictionary of training pages and their corresponding markdown templates training_pages = { @@ -128,9 +142,12 @@ def generate_training_pages(): # Generate markdown for each training page and write it to a file for page_name, page_template in training_pages.items(): page_content = page_template + json.dumps(trainings) - with open(os.path.join(site_config.resources_markdown_path, f"{page_name}.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(site_config.resources_markdown_path, f"{page_name}.md"), "w", encoding="utf8" + ) as md_file: md_file.write(page_content) + def generate_brand_page(): """Responsible for generating the markdown pages of the training pages.""" logger.info("Generating brand") @@ -212,6 +229,7 @@ def generate_faq_page(): with open(os.path.join(site_config.resources_markdown_path, "faq.md"), "w", encoding="utf8") as md_file: md_file.write(faq_content) + def generate_static_pages(): """Reads markdown files from the static pages directory and copies them into the markdown directory.""" logger.info("Generating static pages") @@ -219,7 +237,7 @@ def generate_static_pages(): if not [key["module_name"] for key in modules.run_ptr if key["module_name"] == "versions"]: util.buildhelpers.remove_element_from_sub_menu(resources_config.module_name, "Version History") - + # Below code used to get a list of all updates children updates_dict_list = {} updates_name = [] @@ -289,7 +307,7 @@ def generate_working_with_attack(): "techniques", "datasources", "campaigns", - "assets" + "assets", ] # Verify if directories exists @@ -342,7 +360,9 @@ def generate_sidebar_resources(): sidebar_resources_md = resources_config.sidebar_resources_md # write markdown to file - with open(os.path.join(site_config.resources_markdown_path, "sidebar_resources.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(site_config.resources_markdown_path, "sidebar_resources.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_resources_md) @@ -360,7 +380,7 @@ def generate_contribute_page(): data = {} - data["contributors"] = [] + data["contributors"] = [] contributors_first_col = [] contributors_second_col = [] @@ -384,11 +404,10 @@ def generate_contribute_page(): subs = resources_config.contribute_md + json.dumps(data) # Open markdown file for the contribute page - with open( - os.path.join(site_config.resources_markdown_path, "contribute.md"), "w", encoding="utf8" - ) as md_file: + with open(os.path.join(site_config.resources_markdown_path, "contribute.md"), "w", encoding="utf8") as md_file: md_file.write(subs) + def generate_presentation_archive(): """Responsible for compiling resources json into resources markdown files for rendering on the HMTL.""" logger.info("Generating presentation archive") @@ -408,6 +427,7 @@ def generate_presentation_archive(): ) as md_file: md_file.write(resources_content) + def generate_use_case_page(): """Responsible for compiling use cases json into use cases markdown file for rendering on the HMTL.""" logger.info("Generating get started pages") @@ -424,7 +444,7 @@ def generate_use_case_page(): for i in range(len(use_case_data)): use_case_name.append(use_case_data[i]["title"]) title = "Title: " + use_case_data[i]["title"] + "\n" - name = use_case_data[i]["title"].lower().replace(' ','-').replace("&", "a") + name = use_case_data[i]["title"].lower().replace(" ", "-").replace("&", "a") template = "Template: resources/use-cases\n" use_case_path.append("/resources/get-started/" + name + "/") save_as = "save_as: resources/get-started/" + name + "/index.html\n" @@ -439,6 +459,6 @@ def generate_use_case_page(): use_case_list = use_case_dict_list["use_case_md"] for i in range(len(use_case_list)): use_case_content = use_case_list[i] + json.dumps(use_case_data[i]) - f_name = "use-case-" + use_case_data[i]["title"].lower().replace(' ','-') + ".md" + f_name = "use-case-" + use_case_data[i]["title"].lower().replace(" ", "-") + ".md" with open(os.path.join(site_config.resources_markdown_path, f_name), "w", encoding="utf8") as md_file: - md_file.write(use_case_content) \ No newline at end of file + md_file.write(use_case_content) diff --git a/modules/resources/resources_config.py b/modules/resources/resources_config.py index 8a46608b937..18ef48c3d9c 100644 --- a/modules/resources/resources_config.py +++ b/modules/resources/resources_config.py @@ -76,12 +76,7 @@ ) # FAQ md -faq_md = ( - "Title: Frequently Asked Questions\n" - "Template: general/faq-overview\n" - "save_as: resources/faq/index.html\n" - "data: " -) +faq_md = "Title: Frequently Asked Questions\nTemplate: general/faq-overview\nsave_as: resources/faq/index.html\ndata: " # Contribute md contribute_md = ( @@ -119,7 +114,5 @@ # Sidebar md sidebar_resources_md = ( - "Title: Resources Sidebar\n" - "Template: general/sidebar-resources \n" - "save_as: resources/sidebar-resources/index.html\n" + "Title: Resources Sidebar\nTemplate: general/sidebar-resources \nsave_as: resources/sidebar-resources/index.html\n" ) diff --git a/modules/site_config.py b/modules/site_config.py index 9bb53627140..d62e9e467a6 100644 --- a/modules/site_config.py +++ b/modules/site_config.py @@ -135,17 +135,9 @@ def set_subdirectory(subdirectory_str): # Redirect md string template redirect_md_index = Template( - "Title: ${title}\n" - "Template: general/redirect-index\n" - "RedirectLink: ${to}\n" - "save_as: ${from}/index.html" -) -redirect_md = Template( - "Title: ${title}\n" - "Template: general/redirect-index\n" - "RedirectLink: ${to}\n" - "save_as: ${from}" + "Title: ${title}\nTemplate: general/redirect-index\nRedirectLink: ${to}\nsave_as: ${from}/index.html" ) +redirect_md = Template("Title: ${title}\nTemplate: general/redirect-index\nRedirectLink: ${to}\nsave_as: ${from}") # Custom_alphabet used to sort list of dictionaries by domain name # depending on domain ordering @@ -174,10 +166,7 @@ def set_subdirectory(subdirectory_str): # domain: "enterprise", "mobile", "ics" # path: the path to the object, e.g "software/S1001" or "groups/G2021" layer_md = Template( - "Title: ${domain} Techniques\n" - "Template: general/json\n" - "save_as: ${path}/${attack_id}-${domain}-layer.json\n" - "json: " + "Title: ${domain} Techniques\nTemplate: general/json\nsave_as: ${path}/${attack_id}-${domain}-layer.json\njson: " ) layer_version = "4.5" navigator_version = "5.1.0" diff --git a/modules/software/__init__.py b/modules/software/__init__.py index 103553ec56d..e65709aeede 100644 --- a/modules/software/__init__.py +++ b/modules/software/__init__.py @@ -2,14 +2,17 @@ from . import software_config import json + def get_priority(): return software_config.priority + # TODO commented out to resolve infinite redirect loop when run locally. Needs further testing before code removal. # def get_redirections(): # with open(software_config.software_redirection_location , "r", encoding="utf8") as json_redirections: # return json.load(json_redirections) # return [] + def run_module(): return software.generate_software(), software_config.module_name diff --git a/modules/software/software_config.py b/modules/software/software_config.py index 304e3ca3a6f..e399491f585 100644 --- a/modules/software/software_config.py +++ b/modules/software/software_config.py @@ -10,16 +10,10 @@ software_templates_path = "modules/software/templates/" # String template for software index page -software_index_md = ("Title: Software overview\n" - "Template: software/software-index\n" - "save_as: software/index.html\n" - "data: ") +software_index_md = "Title: Software overview\nTemplate: software/software-index\nsave_as: software/index.html\ndata: " # String template for group page -software_md = Template("Title: ${name}\n" - "Template: software/software\n" - "save_as: software/${attack_id}/index.html\n" - "data: ") +software_md = Template("Title: ${name}\nTemplate: software/software\nsave_as: software/${attack_id}/index.html\ndata: ") software_redirection_location = "modules/software/software_redirections.json" @@ -28,4 +22,4 @@ "Template: general/sidebar-template \n" "save_as: software/sidebar-software/index.html\n" "data: " -) \ No newline at end of file +) diff --git a/modules/tactics/__init__.py b/modules/tactics/__init__.py index 611812692c2..015f14636a8 100644 --- a/modules/tactics/__init__.py +++ b/modules/tactics/__init__.py @@ -21,6 +21,7 @@ def get_menu(): ], } + # TODO resolve infinite redirect loop when run locally. Needs further testing before code removal. def get_redirections(): with open(tactics_config.tactics_redirection_location, "r", encoding="utf8") as json_redirections: diff --git a/modules/tactics/tactics.py b/modules/tactics/tactics.py index 2008ddbd57b..2037c9bfc6d 100644 --- a/modules/tactics/tactics.py +++ b/modules/tactics/tactics.py @@ -190,6 +190,7 @@ def get_techniques_of_tactic(tactic, techniques): techniques_list = sorted(techniques_list, key=lambda k: k["name"].lower()) return techniques_list + def generate_sidebar_tactics(side_nav_data): """Responsible for generating the sidebar for the tactics pages.""" logger.info("Generating tactics sidebar") @@ -200,6 +201,7 @@ def generate_sidebar_tactics(side_nav_data): sidebar_tactics_md = tactics_config.sidebar_tactics_md + json.dumps(data) # write markdown to file - with open(os.path.join(tactics_config.tactics_markdown_path, "sidebar_tactics.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(tactics_config.tactics_markdown_path, "sidebar_tactics.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_tactics_md) - diff --git a/modules/tactics/tactics_config.py b/modules/tactics/tactics_config.py index b8dd5e4fa71..99d3dd261c1 100644 --- a/modules/tactics/tactics_config.py +++ b/modules/tactics/tactics_config.py @@ -11,12 +11,12 @@ # String template for domains tactic_domain_md = Template( - "Title: Tactics\n" "Template: tactics/tactics-domain-index\n" "save_as: tactics/${domain}/index.html\n" "data: " + "Title: Tactics\nTemplate: tactics/tactics-domain-index\nsave_as: tactics/${domain}/index.html\ndata: " ) # String template for tactics tactic_md = Template( - "Title: ${name}-${domain}\n" "Template: tactics/tactic\n" "save_as: tactics/${attack_id}/index.html\n" "data: " + "Title: ${name}-${domain}\nTemplate: tactics/tactic\nsave_as: tactics/${attack_id}/index.html\ndata: " ) # Tactics overview md template @@ -30,8 +30,5 @@ tactics_redirection_location = "modules/tactics/tactics_redirections.json" sidebar_tactics_md = ( - "Title: Tactics Sidebar\n" - "Template: general/sidebar-template \n" - "save_as: tactics/sidebar-tactics/index.html\n" - "data: " -) \ No newline at end of file + "Title: Tactics Sidebar\nTemplate: general/sidebar-template \nsave_as: tactics/sidebar-tactics/index.html\ndata: " +) diff --git a/modules/techniques/techniques.py b/modules/techniques/techniques.py index 3d88ed39f3f..e78bb127e14 100644 --- a/modules/techniques/techniques.py +++ b/modules/techniques/techniques.py @@ -361,15 +361,14 @@ def get_mitigations_table_data(technique, reference_list): if not mitigation["object"].get("x_mitre_deprecated"): attack_id = util.buildhelpers.get_attack_id(mitigation["object"]) # Only add if mitigation attack id is found - if not attack_id: continue + if not attack_id: + continue row = {} row["mid"] = attack_id row["name"] = mitigation["object"]["name"] if mitigation["relationship"].get("description"): # Get filtered description - reference_list = util.buildhelpers.update_reference_list( - reference_list, mitigation["relationship"] - ) + reference_list = util.buildhelpers.update_reference_list(reference_list, mitigation["relationship"]) row["descr"] = mitigation["relationship"]["description"] mitigation_data.append(row) @@ -396,15 +395,14 @@ def get_assets_table_data(technique, reference_list): attack_id = util.buildhelpers.get_attack_id(asset["object"]) # Only add if attack id is found - if not attack_id: continue + if not attack_id: + continue row = {} row["id"] = attack_id row["name"] = asset["object"]["name"] if asset["relationship"].get("description"): # Get filtered description - reference_list = util.buildhelpers.update_reference_list( - reference_list, asset["relationship"] - ) + reference_list = util.buildhelpers.update_reference_list(reference_list, asset["relationship"]) row["descr"] = asset["relationship"]["description"] asset_data.append(row) @@ -436,7 +434,8 @@ def get_examples_table_data(technique, reference_list): attack_id = util.buildhelpers.get_attack_id(example["object"]) # Only add example data if the attack id is found - if not attack_id: continue + if not attack_id: + continue row = {} row["id"] = attack_id @@ -447,9 +446,7 @@ def get_examples_table_data(technique, reference_list): if example["relationship"].get("description"): # Get filtered description - reference_list = util.buildhelpers.update_reference_list( - reference_list, example["relationship"] - ) + reference_list = util.buildhelpers.update_reference_list(reference_list, example["relationship"]) row["descr"] = example["relationship"]["description"] example_data.append(row) @@ -544,7 +541,8 @@ def get_techniques_list(techniques): if not technique.get("revoked") and not technique.get("x_mitre_deprecated"): attack_id = util.buildhelpers.get_attack_id(technique) - if not attack_id: continue + if not attack_id: + continue technique_dict = {} technique_dict["id"] = attack_id @@ -649,6 +647,7 @@ def get_datasources_and_components_of_technique(technique, reference_list): return datasource_and_components, show_descriptions + def generate_sidebar_techniques(side_nav_data): """Responsible for generating the sidebar for the technique pages.""" logger.info("Generating technique sidebar") @@ -659,5 +658,7 @@ def generate_sidebar_techniques(side_nav_data): sidebar_techniques_md = techniques_config.sidebar_techniques_md + json.dumps(data) # write markdown to file - with open(os.path.join(techniques_config.techniques_markdown_path, "sidebar_techniques.md"), "w", encoding="utf8") as md_file: + with open( + os.path.join(techniques_config.techniques_markdown_path, "sidebar_techniques.md"), "w", encoding="utf8" + ) as md_file: md_file.write(sidebar_techniques_md) diff --git a/modules/techniques/techniques_config.py b/modules/techniques/techniques_config.py index 1679960fb7d..399c4f58fbd 100644 --- a/modules/techniques/techniques_config.py +++ b/modules/techniques/techniques_config.py @@ -11,18 +11,12 @@ # String template for all techniques technique_md = Template( - "Title: ${name}-${domain}\n" - "Template: techniques/technique\n" - "save_as: techniques/${attack_id}/index.html\n" - "data: " + "Title: ${name}-${domain}\nTemplate: techniques/technique\nsave_as: techniques/${attack_id}/index.html\ndata: " ) # String template for domains technique_domain_md = Template( - "Title: Techniques\n" - "Template: techniques/techniques-domain-index\n" - "save_as: techniques/${domain}/index.html\n" - "data: " + "Title: Techniques\nTemplate: techniques/techniques-domain-index\nsave_as: techniques/${domain}/index.html\ndata: " ) # Overview md template @@ -48,4 +42,4 @@ "Template: general/sidebar-template \n" "save_as: techniques/sidebar-techniques/index.html\n" "data: " -) \ No newline at end of file +) diff --git a/modules/tests/tests.py b/modules/tests/tests.py index a6b5f592ba7..05c9595064e 100644 --- a/modules/tests/tests.py +++ b/modules/tests/tests.py @@ -207,7 +207,7 @@ def check_size(): MSG = "Approaching 1 GB limit: " + f"{size_MB:.2f} MB" else: STATUS = tests_config.FAILED_STATUS - MSG = "Surpassed 1 GB limit: " f"{size_MB/MB_TO_GB_CONVERSION:.3f} GB" + tests_config.RESET + MSG = f"Surpassed 1 GB limit: {size_MB / MB_TO_GB_CONVERSION:.3f} GB" + tests_config.RESET logger.info(f"STATUS {STATUS} TEST {TEST} MSG {MSG}") diff --git a/modules/util/buildhelpers.py b/modules/util/buildhelpers.py index 20b82e56d26..2bd03ada897 100644 --- a/modules/util/buildhelpers.py +++ b/modules/util/buildhelpers.py @@ -495,16 +495,12 @@ def replace_html_chars(to_be_replaced): colorMap = { - 0: "#ffffff", # techniques not used by the object - 1: "#66b1ff", # techniques used by the object - 2: "#ff6666", # techniques used by inherited campaign relationships - 3: "#ff66f4" # techniques used by the object AND used by inherited campaign relationships (1 & 2) -} -domain_name_map = { - "enterprise-attack": "Enterprise", - "mobile-attack": "Mobile", - "ics-attack": "ICS" + 0: "#ffffff", # techniques not used by the object + 1: "#66b1ff", # techniques used by the object + 2: "#ff6666", # techniques used by inherited campaign relationships + 3: "#ff66f4", # techniques used by the object AND used by inherited campaign relationships (1 & 2) } +domain_name_map = {"enterprise-attack": "Enterprise", "mobile-attack": "Mobile", "ics-attack": "ICS"} def get_navigator_layers(name, attack_id, obj_type, rel_type, version, techniques_used, inheritance=False): @@ -526,10 +522,13 @@ def get_navigator_layers(name, attack_id, obj_type, rel_type, version, technique color = technique["color"] if technique.get("color") else 0 has_subtechniques = True if technique.get("subtechniques") else False score = 1 if technique.get("descr") else 0 - technique_layer_object = get_technique_layer_object(technique["id"], description, score, color, has_subtechniques) + technique_layer_object = get_technique_layer_object( + technique["id"], description, score, color, has_subtechniques + ) # Skip this technique if no layer object - if not technique_layer_object: continue + if not technique_layer_object: + continue # Add technique layer object to domain layer if "enterprise" in technique["domain"]: @@ -560,26 +559,32 @@ def get_navigator_layers(name, attack_id, obj_type, rel_type, version, technique # Build list of domains with navigator layers layers = [] if enterprise_layer["techniques"]: - layers.append({ - "domain": "enterprise", - "name": domain_name_map["enterprise-attack"], - "filename": f"{attack_id}-enterprise-layer.json", - "layer": json.dumps(enterprise_layer) - }) + layers.append( + { + "domain": "enterprise", + "name": domain_name_map["enterprise-attack"], + "filename": f"{attack_id}-enterprise-layer.json", + "layer": json.dumps(enterprise_layer), + } + ) if mobile_layer["techniques"]: - layers.append({ - "domain": "mobile", - "name": domain_name_map["mobile-attack"], - "filename": f"{attack_id}-mobile-layer.json", - "layer": json.dumps(mobile_layer) - }) + layers.append( + { + "domain": "mobile", + "name": domain_name_map["mobile-attack"], + "filename": f"{attack_id}-mobile-layer.json", + "layer": json.dumps(mobile_layer), + } + ) if ics_layer["techniques"]: - layers.append({ - "domain": "ics", - "name": domain_name_map["ics-attack"], - "filename": f"{attack_id}-ics-layer.json", - "layer": json.dumps(ics_layer) - }) + layers.append( + { + "domain": "ics", + "name": domain_name_map["ics-attack"], + "filename": f"{attack_id}-ics-layer.json", + "layer": json.dumps(ics_layer), + } + ) return layers @@ -589,7 +594,9 @@ def build_base_layer(domain, object_name, object_type, rel_type, attack_id, vers layer = {} # Layer description - layer["description"] = f"{domain_name_map[domain]} techniques {rel_type} {object_name}, ATT&CK {object_type} {attack_id}" + layer["description"] = ( + f"{domain_name_map[domain]} techniques {rel_type} {object_name}, ATT&CK {object_type} {attack_id}" + ) if version: # Add object version number if it exists layer["description"] += f" (v{version})" @@ -615,16 +622,19 @@ def build_base_layer(domain, object_name, object_type, rel_type, attack_id, vers } # Layer legend - layer["legendItems"] = [ - {"label": f"{rel_type} {object_name}", "color": colorMap[1]} - ] + layer["legendItems"] = [{"label": f"{rel_type} {object_name}", "color": colorMap[1]}] # Add campaign inheritance to legend, if applicable if inheritance: - layer["legendItems"].extend([ - {"label": f"{rel_type} a campaign attributed to {object_name}", "color": colorMap[2]}, - {"label": f"{rel_type} {object_name} and {rel_type} a campaign attributed to {object_name}", "color": colorMap[3]} - ]) + layer["legendItems"].extend( + [ + {"label": f"{rel_type} a campaign attributed to {object_name}", "color": colorMap[2]}, + { + "label": f"{rel_type} {object_name} and {rel_type} a campaign attributed to {object_name}", + "color": colorMap[3], + }, + ] + ) return layer @@ -645,7 +655,7 @@ def get_technique_layer_object(attack_id, description, score, color, showSub=Fal navigator_technique["score"] = score if color and color in colorMap.keys(): navigator_technique["color"] = colorMap[color] - + # show subtechniques? navigator_technique["showSubtechniques"] = showSub @@ -689,7 +699,7 @@ def print_end(name, start_time, end_time): hyphens = "-" * number_of_hyphens # spaces here because we need to overwrite the word "running" - sys.stdout.write(f"\r{name: <{name_space}} : {hyphens} {end_time-start_time:.2f}s \n") + sys.stdout.write(f"\r{name: <{name_space}} : {hyphens} {end_time - start_time:.2f}s \n") sys.stdout.flush() diff --git a/modules/util/relationshipgetters.py b/modules/util/relationshipgetters.py index ff92451a1a0..f6868cd10f5 100644 --- a/modules/util/relationshipgetters.py +++ b/modules/util/relationshipgetters.py @@ -152,6 +152,7 @@ def get_assets_targeted_by_techniques(): return assets_targeted_by_techniques + def get_techniques_detected_by_datacomponent(): global techniques_detected_by_datacomponent @@ -453,6 +454,7 @@ def get_campaign_list(): return campaign_list + def get_asset_list(): """asset list getter""" global asset_list diff --git a/modules/util/relationshiphelpers.py b/modules/util/relationshiphelpers.py index 3a8d42db620..eeaff15d9ae 100644 --- a/modules/util/relationshiphelpers.py +++ b/modules/util/relationshiphelpers.py @@ -255,6 +255,7 @@ def techniques_targeting_assets(srcs): """ return get_related(srcs, "attack-pattern", "targets", "x-mitre-asset", reverse=True) + def assets_targeted_by_techniques(srcs): """Return technique_id => {asset, relationship} for each asset targeted by the technique. @@ -262,6 +263,7 @@ def assets_targeted_by_techniques(srcs): """ return get_related(srcs, "attack-pattern", "targets", "x-mitre-asset") + # technique:malware def techniques_used_by_malware(srcs): """Return malware => {technique, relationship} for each technique used by the malware. diff --git a/modules/versions/versions_config.py b/modules/versions/versions_config.py index 0ca96ad20f1..8bc15a181f7 100644 --- a/modules/versions/versions_config.py +++ b/modules/versions/versions_config.py @@ -16,6 +16,4 @@ versions_repo = "https://github.com/mitre-attack/attack-website.git" versions_directory = "attack-versions" -versions_md = ( - "Title: Version History\n" "Template: versions/versions\n" "save_as: resources/versions/index.html\n" "data: " -) +versions_md = "Title: Version History\nTemplate: versions/versions\nsave_as: resources/versions/index.html\ndata: " diff --git a/modules/website_build/website_build_config.py b/modules/website_build/website_build_config.py index 46c9f50636e..b9c76d4a00b 100644 --- a/modules/website_build/website_build_config.py +++ b/modules/website_build/website_build_config.py @@ -57,7 +57,7 @@ } # ATT&CK overview -attack_index_md = "Title: ATT&CK Overview \n" "Template: general/attack-index \n" "save_as: index.html\n" "data: " +attack_index_md = "Title: ATT&CK Overview \nTemplate: general/attack-index \nsave_as: index.html\ndata: " # ATT&CK index markdown path attack_index_path = "content/pages/index.md" @@ -71,4 +71,4 @@ website_build_templates_path = "modules/website_build/templates/" # CHANGELOG md -changelog_md = "Title: Changelog\n" "Template: website_build/changelog\n" "save_as: resources/changelog.html\n\n" +changelog_md = "Title: Changelog\nTemplate: website_build/changelog\nsave_as: resources/changelog.html\n\n" diff --git a/pelicanconf.py b/pelicanconf.py index 4c74492c07b..320118d5e71 100644 --- a/pelicanconf.py +++ b/pelicanconf.py @@ -8,20 +8,20 @@ # read file with custom jinja filters import sys -sys.path.append('.') +sys.path.append(".") import custom_jinja_filters -AUTHOR = os.environ.get('PELICAN_AUTHOR', 'MITRE') -SITENAME = os.environ.get('PELICAN_SITENAME', 'ATT&CK') -SITEURL = os.environ.get('PELICAN_SITEURL', '') +AUTHOR = os.environ.get("PELICAN_AUTHOR", "MITRE") +SITENAME = os.environ.get("PELICAN_SITENAME", "ATT&CK") +SITEURL = os.environ.get("PELICAN_SITEURL", "") -PATH = 'content' +PATH = "content" -TIMEZONE = os.environ.get('PELICAN_TIMEZONE', 'America/New_York') +TIMEZONE = os.environ.get("PELICAN_TIMEZONE", "America/New_York") -DEFAULT_LANG = os.environ.get('PELICAN_DEFAULT_LANG','en') +DEFAULT_LANG = os.environ.get("PELICAN_DEFAULT_LANG", "en") -THEME = 'attack-theme' +THEME = "attack-theme" # Feed generation is usually not desired when developing FEED_ALL_ATOM = None @@ -31,18 +31,18 @@ AUTHOR_FEED_RSS = None DEFAULT_PAGINATION = False -STATIC_PATHS = ['docs'] -ARTICLE_PATHS = ['pages/updates'] +STATIC_PATHS = ["docs"] +ARTICLE_PATHS = ["pages/updates"] # Uncomment following line if you want document-relative URLs when developing RELATIVE_URLS = False JINJA_FILTERS = { - 'from_json': custom_jinja_filters.json.loads, - 'flatten_tree': custom_jinja_filters.flatten_tree, - 'clean_path': custom_jinja_filters.clean_path, - 'remove_whitespace': custom_jinja_filters.remove_whitespace, - 'escape_spaces': custom_jinja_filters.escape_spaces, - 'stixToHTML': custom_jinja_filters.stixToHTML, - 'permalink': custom_jinja_filters.permalink + "from_json": custom_jinja_filters.json.loads, + "flatten_tree": custom_jinja_filters.flatten_tree, + "clean_path": custom_jinja_filters.clean_path, + "remove_whitespace": custom_jinja_filters.remove_whitespace, + "escape_spaces": custom_jinja_filters.escape_spaces, + "stixToHTML": custom_jinja_filters.stixToHTML, + "permalink": custom_jinja_filters.permalink, } diff --git a/update-attack.py b/update-attack.py index 4b4fc048bdb..4e0565bdc32 100644 --- a/update-attack.py +++ b/update-attack.py @@ -230,7 +230,7 @@ def remove_from_menu(): ptr["run_module"]() end_time = time.time() util.buildhelpers.print_end(ptr["module_name"], start_time, end_time) - + # Print end of module update_end = time.time() util.buildhelpers.print_end("TOTAL Update Time", update_start, update_end) From 04a94ca1882203c1302a7035077d7ad6f564fba3 Mon Sep 17 00:00:00 2001 From: Evan Lucchesi Leon <189633144+elucchesileon@users.noreply.github.com> Date: Tue, 27 May 2025 09:47:23 -0400 Subject: [PATCH 02/11] fix: get rid of 'SyntaxWarning: invalid escape sequence' errors --- custom_jinja_filters.py | 2 +- modules/stixtests/linkbyidchecker.py | 2 +- modules/subdirectory/subdirectory.py | 24 +----------- modules/techniques/techniques.py | 2 +- modules/tests/citationchecker.py | 2 +- modules/tests/linkchecker.py | 58 ++++------------------------ modules/util/buildhelpers.py | 2 +- modules/versions/versions.py | 30 +++----------- 8 files changed, 20 insertions(+), 102 deletions(-) diff --git a/custom_jinja_filters.py b/custom_jinja_filters.py index d93c61fc415..83cde0013c0 100644 --- a/custom_jinja_filters.py +++ b/custom_jinja_filters.py @@ -71,7 +71,7 @@ def clean_stix_data(data): def get_citations(data): """Given a description, find all of the citations.""" - p = re.compile("\(Citation: (.*?)\)") + p = re.compile(r"\(Citation: (.*?)\)") return p.findall(data) diff --git a/modules/stixtests/linkbyidchecker.py b/modules/stixtests/linkbyidchecker.py index 04de5b8e15d..d66268fbb01 100644 --- a/modules/stixtests/linkbyidchecker.py +++ b/modules/stixtests/linkbyidchecker.py @@ -126,7 +126,7 @@ def linkbyid_check(): "Object with broken data": pretty_name, "Section found in": "Description", # sort of abusing "Unknown LinkById" here, but it works well enough - "Unknown LinkById": f"{{{{LinkById\|{attack_id_in_description}}}}}", + "Unknown LinkById": rf"{{{{LinkById|{attack_id_in_description}}}}}", } link_by_id_warnings.append(warning) diff --git a/modules/subdirectory/subdirectory.py b/modules/subdirectory/subdirectory.py index 0b42bcb7101..ad9c67d7d28 100644 --- a/modules/subdirectory/subdirectory.py +++ b/modules/subdirectory/subdirectory.py @@ -11,27 +11,7 @@ def generate_subdirectory(): replace() -allowed_in_link = "".join( - list( - map( - lambda s: s.strip(), - [ - " - ", - " ? ", - " \w ", - " \\ ", - " $ ", - " \. ", - " ! ", - " \* ", - " ' ", - " () ", - " / ", - ], - ) - ) -) - +allowed_in_link = r"-?\w\$\.!\*'()/" def replace_links(filepath): """In the given file, replace the in-site links to reference @@ -43,7 +23,7 @@ def replace_links(filepath): html_str = html.read() # subdirectory link format - dest_link_format = f"/{site_config.subdirectory}\g<1>" + dest_link_format = rf"/{site_config.subdirectory}\g<1>" def substitute(prefix, html_str): from_str = f"{prefix}=[\"']([{allowed_in_link}]+)[\"']" diff --git a/modules/techniques/techniques.py b/modules/techniques/techniques.py index e78bb127e14..3f3fb7918be 100644 --- a/modules/techniques/techniques.py +++ b/modules/techniques/techniques.py @@ -259,7 +259,7 @@ def generate_data_for_md(technique_dict, technique, tactic_list, is_sub_techniqu if technique.get("x_mitre_system_requirements"): technique["x_mitre_system_requirements"].sort() technique_dict["sysreqs"] = ", ".join(technique["x_mitre_system_requirements"]) - technique_dict["sysreqs"] = re.sub("\.?\\n+", "; ", technique_dict["sysreqs"]) + technique_dict["sysreqs"] = re.sub(r"\.?\\n+", "; ", technique_dict["sysreqs"]) # Get permissions required if technique.get("x_mitre_permissions_required"): diff --git a/modules/tests/citationchecker.py b/modules/tests/citationchecker.py index d4870fa55a8..2509b19e7a7 100644 --- a/modules/tests/citationchecker.py +++ b/modules/tests/citationchecker.py @@ -7,7 +7,7 @@ from . import tests_config -potential_issues_list = ["\(Citation: ?[^)]+\)?"] +potential_issues_list = [r"\(Citation: ?[^)]+\)?"] def citations_check(): diff --git a/modules/tests/linkchecker.py b/modules/tests/linkchecker.py index 9cd1f339a31..717574f177d 100644 --- a/modules/tests/linkchecker.py +++ b/modules/tests/linkchecker.py @@ -10,52 +10,10 @@ from . import tests_config # STATIC PROPERTIES -# The spaces here are for readability with symbol characters. -# The strings are stripped of whitespace. -# These get compiled into a regex string - -allowed_in_link_with_external_links = "".join( - list( - map( - lambda s: s.strip(), - [ - " - ", - " ? ", - " \w ", - " \\ ", - " $ ", - " \. ", - " ! ", - " \* ", - " ' ", - " () ", - " / ", - " : ", - ], - ) - ) -) - -allowed_in_link = "".join( - list( - map( - lambda s: s.strip(), - [ - " - ", - " ? ", - " \w ", - " \\ ", - " $ ", - " \. ", - " ! ", - " \* ", - " ' ", - " () ", - " / ", - ], - ) - ) -) + +allowed_in_link_with_external_links = r"-?\w\$\.!\*'()/:" + +allowed_in_link = r"-?\w\$\.!\*'()/" links_list = {} in_use_links = {} @@ -114,7 +72,7 @@ def get_correct_link(path): "docx", "rtf", ]: - if re.search("(css|js)\?[\w\d]+", sort_of_extension): + if re.search(r"(css|js)\?[\w\d]+", sort_of_extension): # CSS & JavaScript: check for cache-disabling query string suffix, e.g style.min.css?f8be4c06 path = path.split("?")[0] # remove suffix else: @@ -201,9 +159,9 @@ def internal_external_link_checker(filepath, html_str): if ( "/versions/" in filepath ): # don't check links with data-test-ignore attribute, or live version link name, when on previous versions - linkregex = f'{prefix}\s?=\s?["\']([{allowed_in_link_with_external_links}]+)["\'](?! ?data-test-ignore="true")(?!>live version)' + linkregex = rf'{prefix}\s?=\s?["\']([{allowed_in_link_with_external_links}]+)["\'](?! ?data-test-ignore="true")(?!>live version)' else: - linkregex = f"{prefix}\s?=\s?[\"']([{allowed_in_link_with_external_links}]+)[\"']" + linkregex = rf"{prefix}\s?=\s?[\"']([{allowed_in_link_with_external_links}]+)[\"']" links = re.findall(linkregex, html_str) # check if link has a dest @@ -262,7 +220,7 @@ def internal_link_checker(filepath, html_str): # find all links for prefix in ["href", "src"]: - links = re.findall(f"{prefix}\s?=\s?[\"']([{allowed_in_link}]+)[\"']", html_str) + links = re.findall(rf"{prefix}\s?=\s?[\"']([{allowed_in_link}]+)[\"']", html_str) # check if link has a dest for link in links: # Check if link is relative path diff --git a/modules/util/buildhelpers.py b/modules/util/buildhelpers.py index 2bd03ada897..94b952c059b 100644 --- a/modules/util/buildhelpers.py +++ b/modules/util/buildhelpers.py @@ -134,7 +134,7 @@ def update_reference_list(reference_list, obj): def get_reference_set(reflist): """This function retrieves the unique set of references in the given list of descriptions and returns them in string format to be displayed as citations.""" - p = re.compile("\(Citation: (.*?)\)") + p = re.compile(r"\(Citation: (.*?)\)") citations = {} for c in reflist: citations_in_ref = p.findall(c) diff --git a/modules/versions/versions.py b/modules/versions/versions.py index c7f965ba4e7..3f8193c61f3 100644 --- a/modules/versions/versions.py +++ b/modules/versions/versions.py @@ -53,27 +53,7 @@ def onerror(func, path, exc_info): # allowed characters inside of hyperlinks -allowed_in_link = "".join( - list( - map( - lambda s: s.strip(), - [ - " - ", - " ? ", - " \w ", - " \\ ", - " $ ", - " \. ", - " ! ", - " \* ", - " ' ", - " () ", - " / ", - ], - ) - ) -) - +allowed_in_link = r"-?\w\$\.!\*'()/" def versionPath(version): # get the path of a given version @@ -236,10 +216,10 @@ def saferemove(path, type): html_str = html.read() # helper function to substitute links so that they point to /versions/ - dest_link_format = f"/{version_url_path}\g<1>" + dest_link_format = rf"/{version_url_path}\g<1>" def substitute(prefix, html_str): - fromstr = f"{prefix}=[\"'](?!\/versions\/)([{allowed_in_link}]+)[\"']" + fromstr = rf"{prefix}=[\"'](?!\/versions\/)([{allowed_in_link}]+)[\"']" tostr = f'{prefix}="{dest_link_format}"' return re.sub(fromstr, tostr, html_str) @@ -262,8 +242,8 @@ def substitute_redirection(prefix, html_str): # update versioning button to show the permalink site version, aka "back to main site" html_str = html_str.replace("version-button live", "version-button permalink") # update live version links on the versioning button - from_str = f"href=[\"']\/versions\/v[\w-]+\/([{allowed_in_link}]+)[\"'](.*)>[Ll]ive [Vv]ersion<\/a>" - to_str = f'href="/\g<1>"\g<2>>Live Version' + from_str = rf"href=[\"']\/versions\/v[\w-]+\/([{allowed_in_link}]+)[\"'](.*)>[Ll]ive [Vv]ersion<\/a>" + to_str = rf'href="/\g<1>"\g<2>>Live Version' html_str = re.sub(from_str, to_str, html_str) # remove banner message if it is present From 1a7035d79fc2fec14e99a9a3441b8c2cc02824ee Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 27 May 2025 13:36:45 -0400 Subject: [PATCH 03/11] update privacy policy --- .../resources/static_pages/privacy-policy.md | 155 ++++++++++++++---- 1 file changed, 120 insertions(+), 35 deletions(-) diff --git a/modules/resources/static_pages/privacy-policy.md b/modules/resources/static_pages/privacy-policy.md index 762cede8ce6..a31119a8e5c 100644 --- a/modules/resources/static_pages/privacy-policy.md +++ b/modules/resources/static_pages/privacy-policy.md @@ -8,40 +8,125 @@ save_as: resources/legal-and-branding/privacy/index.html **Effective Date: 1 July 2021** -The MITRE Corporation ("MITRE") respects the privacy of its website users. - -This Privacy Policy explains the types of information collected by MITRE ATT&CK® from website visitors ("User") or that you provide to MITRE through other means, such as via e-mail and web forms (collectively "Site"), and how MITRE uses, shares, protects, and retains that information. By visiting the Site, you understand and agree to terms outlined in this Privacy Policy. - -## Information Collected from Cookies on Web Traffic Reporting and Content Tools - -"Cookies" are data that may be sent to your web browser and stored on your computer. Most web browsers can be configured not to accept cookies, or to notify you if a cookie is sent to you. If you wish not to have cookies set during your visit to this MITRE managed Site, you can disable them in your web browser. - -The MITRE ATT&CK website is hosted on GitHub®. MITRE and GitHub use a free third-party software service called Google Analytics® to capture and analyze non-personally identifiable website usage information. - -MITRE logs all accesses and the following information is recorded for each Site user: IP address, date and time of access, the requested URL, the referring URL (if provided by the Web browser), and the browser type (if provided by the Web browser). The IP address and its associated domain name (if any) are used to determine broad demographic information. The IP address may be used to track how users navigate through the Site. - -MITRE additionally collects User-entered keyword search strings to gauge User interest in specific types of vulnerability information. Each access of an individual ATT&CK entry or candidate page is used to gauge User interest. Web log information may be provided to limited research groups within MITRE to support research related to the World Wide Web. This information does not identify you personally. MITRE may store such information, or it may be included in databases owned and maintained by our service providers. - -For specific information about GitHub's collection and use of information collected from Cookies on web traffic reporting and content tools, please review GitHub's Terms of Use and Privacy Policy at [https://docs.github.com/en/github/site-policy](https://docs.github.com/en/github/site-policy). - -## Information Collected from User Subscriptions, E-mail, and Web Forms - -Information that User subscribers may provide, such as company name, location, or job function, is used to determine broad demographic and non-personally identifiable information regarding the types of users of these mailing lists. User subscribers are not required to provide this information. MITRE may provide broad non-personally identifiable demographic information to other organizations. - -ATT&CK-related mailing lists that are sponsored by MITRE are configured to prevent attackers from identifying the subscribers to such mailing lists. - -Any User personal information provided to MITRE for inclusion on subscription mailing lists for ATT&CK evaluations will be governed under the MITRE Engenuity® privacy policy, [https://mitre-engenuity.org/privacy](https://mitre-engenuity.org/privacy). - -Users may contact MITRE electronically, via the [Contact Us](https://www.mitre.org/contact-us) page. MITRE may share the information that you provide to us via e-mail within the corporation to respond to your queries, but we do not provide information to anyone outside of the corporation unless required by law to do so. - -## Due Diligence for Intrusion Detection, Prevention, and Reporting - -MITRE performs due diligence to preserve the integrity of the information on the Site. MITRE uses various logging and tracking mechanisms to support the detection, reporting, or recovery from attempted intrusions into attack.mitre.org. MITRE reserves the right to use all available technologies without notice to protect its networks from unauthorized use, and to report attempted intrusions to the appropriate authorities. - -## Information Collected from Third Party Software and Media Sites - -When Users visit the Site, Users may link to third party software and/or media sites when they link to another party's website. MITRE does not collect any information that may be collected by that third party; however, information you supply to that third-party software may be collected and/or used by that party. For information about that third party's privacy policy, please see their respective website. +This Privacy Policy explains the types of personal information that The MITRE Corporation (“MITRE ,” “we,” “our,” “us”) collects from visitors to the ATT&CK® website (the “Site”) at [https://attack.mitre.org](https://attack.mitre.org); how MITRE uses, shares, protects, stores, and otherwise processes that personal information; and your choices with respect to our use of your personal information. By using our Site, you acknowledge that you understand and agree to the terms outlined in this Privacy Policy. If you have any questions, you may contact us using the information provided at the end of this Privacy Policy. + +## Personal Information We Collect +### Personal Information You Give Us +MITRE may obtain your personal information when you interact with our Site, for example, when you request information using the “Contact Us” link. Personal information is data that identifies you, or could reasonably be used to identify you, as an individual, such as your name, postal address, email address, and phone number. + +### Information We Collect Automatically +We also may collect other information about your visits to our Site using automated tools; for example, cookies and other passive information collection technologies enable MITRE to compile aggregate statistics concerning use of the Site, analyze trends, enhance the security of the Site, deliver content, and otherwise administer and improve the Site. This information may include your browser type, language preference, operating system, device identifier, device type, access time, Internet Protocol (IP) address, the URLs of websites you visited before and after visiting our Site, the web search that landed you on our Site, length of your visits to our Site, and the links you click and pages you visit within our Site. +Your web browser may have settings that allow you to transmit a “Do Not Track” signal when you visit various websites or use online services. Like many websites, our Site is not designed to respond to “Do Not Track” signals received from browsers. To learn more about online tracking, the Federal Trade Commission (FTC) provides guidance on [How To Protect Your Privacy Online](https://www.consumer.ftc.gov/articles/how-protect-your-privacy-online). + +The MITRE ATT&CK website is hosted on GitHub®, which provides internet hosting for software development and version control. When GitHub is visited, the visitor's IP address is logged and stored for security purposes, regardless of whether the visitor has signed into GitHub or not. For more information about GitHub's privacy and security practices, see the [GitHub Privacy Statement](https://docs.github.com/en/github/site-policy/github-privacy-statement). MITRE and GitHub use a free third-party software service called Google Analytics® to help us understand and analyze how visitors use our Site. For more information on how Google Analytics uses data collected through the Site, visit [https://www.google.com/policies/privacy/partners/](https://www.google.com/policies/privacy/partners/). To opt out of Google Analytics cookies, visit: [https://www.google.com/settings/ads](https://www.google.com/settings/ads) and [https://tools.google.com/dlpage/gaoptout/](https://tools.google.com/dlpage/gaoptout/). + +## How We Use Personal Information +MITRE may use personal information we collect through our Site to: +* communicate with you, including to respond to your questions and requests, send you notices about our services, or contact you for additional information when needed; +* analyze Site trends, usage, and the activities of Site visitors; +* improve our Site and notify you about important updates; +* perform internal business analyses or for other business purposes consistent with our mission; +* facilitate, manage, personalize, and improve our partnership relationships; +* identify, prevent, investigate, and take other actions with respect to suspected or actual fraud or illegal activity or other activity that violates our policies; +* ensure the security and integrity of our personal information processing; +* comply with applicable laws, rules, regulations, and legal processes as well as our company policies; and +* fulfill other purposes, with your consent (as required). + +## How We Share Personal Information +MITRE may share your personal information within our organization to: +* better respond to your inquiries; +* perform marketing research and for sales, support, and service-related purposes; +* protect rights, property, life, health, security, and safety; +* negotiate or complete any proposed or actual merger, purchase, sale, or any other type of acquisition or other transaction, including a transfer of all or a portion of our business to another organization; +* disclose personal information with your consent or at your direction; and +* achieve any other purpose consistent with our statements in this Privacy Policy or otherwise allowed by applicable law. + +MITRE may disclose your personal information to comply with applicable law, such as in response to requests from law enforcement agencies, regulators, other public authorities, courts, and third-party litigants in connection with legal proceedings or investigations. + +## Linked Websites +Our Site may include links to other websites that are not owned or operated by MITRE. This Privacy Policy does not apply to those websites, which may have their own privacy policies that you should review to understand how they may collect, use, or disclose your personal information. MITRE is not responsible for the content or privacy practices of any linked websites that it does not control. + +## Security of Personal Information +MITRE maintains reasonable safeguards designed to protect personal information from loss, theft, misuse, and unauthorized access, disclosure, alteration, and destruction. MITRE employs encryption technologies and user authentication procedures that are designed to keep data secure. Nevertheless, transmission via the Internet and online digital storage are not completely secure, so we cannot guarantee the security of your personal information. + +## Information for Visitors from Outside the United States +MITRE is based in the United States. If you are visiting our Site from outside the United States, please be aware that information we obtain about you may be transferred to and processed in the United States or other jurisdictions. By using the Site and providing your personal information, you acknowledge that your personal information may be transferred to and processed in jurisdictions outside your own. Please be aware that the data protection laws and regulations that may apply to your personal information transferred to the United States or other countries may be different from the laws in your country of residence. + +## Information for Visitors from Australia +We are committed to handling your personal information in an open and transparent manner in accordance with applicable laws and regulations. For more information on your privacy rights, you can visit the website of The Office of the Australian Information Commissioner at [www.oaic.gov.au/](www.oaic.gov.au/). + +## Information for Visitors from the European Economic Area and the United Kingdom +This section provides a GDPR Notice (“Notice”) for residents of the European Economic Area (“EEA”) and United Kingdom (“UK”) regarding their respective rights under the European Union’s General Data Protection Regulation and the United Kingdom’s General Data Protection Regulation (collectively, the “GDPR”). MITRE is the data controller for personal data collected through the Site. + +This Notice supplements the information in this Privacy Policy and other MITRE privacy policies and notices. If there is a conflict between any other MITRE privacy policy, statement, or notice and this Notice, this Notice will prevail. + +### Our Collection and Use of Personal Data +Personal data collected through this Site may include: +* Contact Data. You may provide your contact details, such as your name, phone number, postal address, email address, and company affiliation; for example, when you contact us for further information or subscribe to receive our news and information offerings. +* Device Data. We may obtain information about devices that access our Site, including the type of device, operating system, device settings, unique device identifiers, and error data. +* Other Data You Provide. This includes personal data you include in communications you send to us, such as inquiries about our services. + +### Our Processing of Your Personal Data +Your personal data is required for us to provide some of our services. In some instances, if you fail to provide your personal data, you may not be able to access or use our services. We may process the personal data you provide for any of the purposes identified in the “How We Use Personal Information” and “How We Share Personal Information” Sections of this Privacy Policy. + +Your personal data is processed pursuant to the following legal bases: +* The processing is necessary for us to provide you with the services you request or to respond to your questions. +* We have a legal obligation to process your personal data, such as compliance with applicable tax laws or other government regulations or compliance with a court order or binding law enforcement request. +* We have a legitimate interest in processing your personal data and our reasons for using the personal data outweigh the potential prejudice to your data protection rights. In particular, we have a legitimate interest in the following instances: + * To analyze and improve the safety and security of our Site and services, including by implementing and enhancing security measures and safeguards and protecting against fraud, spam, and other abuses; + * To maintain and improve our Site and services; and + * To operate and promote MITRE’s services and provide you with information and communications about our services that are tailored to, and in accordance with, your preferences. +* You have consented to our processing of your personal data. When you consent, you may change your mind and withdraw your consent at any time by emailing us at [privacy@mitre.org](privacy@mitre.org). + +### Your Rights Under the GDPR +The GDPR provides individuals with certain rights regarding their personal data. You may ask us to take the following actions: +* provide you with information about our processing of your personal data and access to your personal data; +* update or correct inaccuracies in your personal data; +* delete your personal data; +* transfer a copy of your personal data to you or a third party of your choice; +* restrict the processing of your personal data; +* object to our use of your personal data for marketing purposes; and +* object to our reliance on legitimate interests as the basis for processing your personal data. + +You may submit these requests by email to [privacy@mitre.org](privacy@mitre.org). We may require specific information from you to help us verify your identity prior to processing your request. Applicable law may require or permit us to decline your request. If we decline your request, we will tell you why, subject to any legal restrictions on disclosing this information. + +If you would like to submit a complaint about our use of your personal data or our response to your request regarding your personal data, you may contact us at [privacy@mitre.org](privacy@mitre.org) or submit a complaint directly to the data protection authority in your jurisdiction. If you reside in the EEA, you can find information about your data protection authority [here](https://edpb.europa.eu/about-edpb/about-edpb/members_en). If you reside in the UK, you may file complaints with the Information Commissioner’s Office [here](https://ico.org.uk/make-a-complaint/). + +### Our Retention of Your Personal Data +MITRE retains your personal data for no longer than is necessary to achieve the purposes for which the personal data was collected, or as may otherwise be permitted or required under applicable law. To determine the appropriate retention period, we will consider the scope and sensitivity of the personal data; the potential risk of harm from unauthorized access to, use, or disclosure of the data; the purposes for which we process the data; whether we can achieve our purposes through other means; our business needs; and applicable legal requirements. Unless otherwise required by applicable law, at the end of the retention period, we will anonymize or securely destroy your personal data. + +### Personal Data Transfers +By using this Site, you acknowledge that your personal data may be collected, transferred to, and processed in jurisdictions outside your own. When you directly provide your personal data through our Site, you acknowledge that your personal data is being provided by you to a company based in the United States. The laws that apply to personal data protection in the United States differ from those applicable in the EEA and the UK. +If it is necessary for us to transfer personal data out of the EEA and the UK, we do so by using suitable data transfer mechanisms, such as the standard contractual clauses approved by the European Commission, which impose data protection obligations on parties to the transfer. + +## Information for Specific Individuals +Residents of U.S. states with consumer privacy laws in effect and enforceable may contact us at [privacy@mitre.org](privacy@mitre.org) for further information about our privacy practices. + +## Privacy of Children +This Site is not intended for children, and we do not knowingly collect personal information from children. If we become aware that we have collected personal information from a child, we will delete it in accordance with applicable law. ## Changes to Our Privacy Policy - -The Site may change from time to time. As a result, at times it may be necessary for us to make changes to this Privacy Policy. Accordingly, MITRE reserves the right to update or modify this Privacy Policy at any time and from time to time without prior notice. Please review this policy periodically, and especially before you provide any information. Your continued use of the Site after any changes or revisions to this Privacy Policy shall indicate your agreement with the terms of such revised Privacy Policy. +MITRE may update or modify this Privacy Policy from time to time at our discretion. We will indicate changes to this Privacy Policy by updating the “Effective Date” at the beginning of the Privacy Policy. Please review this Privacy Policy periodically and especially before you provide any personal information to us. Your continued use of this Site after any update to this Privacy Policy will constitute your acceptance of our changes. + +## Questions +If you have questions about this Privacy Policy or MITRE’s privacy practices, you may email [privacy@mitre.org](privacy@mitre.org). +MITRE’s Data Protection Officer for Singapore may be contacted as follows: + +In the United States + +Dena Kozanas – Data Protection Officer\ +Associate General Counsel & Chief Privacy Official\ +7515 Colshire Drive\ +McLean, VA 22102\ +Phone: +1 (703) 269-8515\ +Email: privacy@mitre.org + +In Singapore + +MITRE Asia Pacific Singapore\ +Thomas (Tass) Bruce Hudak – Privacy Coordinator\ +1 Changi Business Park Avenue 1\ +Suite #02-03/04\ +Singapore 486058\ +Phone: +65 8876 4609\ +Email: [privacy@mitre.org](mailto: privacy@mitre.org) \ No newline at end of file From 3d9db5eaf3be2f2c5aa3a1f8717e1c41cc1a1029 Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 27 May 2025 18:12:25 -0400 Subject: [PATCH 04/11] add osano --- .../templates/general/base-template.html | 9 +++- .../resources/static_pages/privacy-policy.md | 44 +++++++++---------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/attack-theme/templates/general/base-template.html b/attack-theme/templates/general/base-template.html index 7ca955c35de..02a06001e1d 100644 --- a/attack-theme/templates/general/base-template.html +++ b/attack-theme/templates/general/base-template.html @@ -23,6 +23,10 @@ {% block head %} + + {% include "general/google-analytics.html" %} {% include "general/google-site-verification.html" %} @@ -106,9 +110,12 @@ Privacy Policy {% endif %} -
diff --git a/modules/resources/static_pages/privacy-policy.md b/modules/resources/static_pages/privacy-policy.md index a31119a8e5c..141a27e691c 100644 --- a/modules/resources/static_pages/privacy-policy.md +++ b/modules/resources/static_pages/privacy-policy.md @@ -53,7 +53,7 @@ MITRE maintains reasonable safeguards designed to protect personal information f MITRE is based in the United States. If you are visiting our Site from outside the United States, please be aware that information we obtain about you may be transferred to and processed in the United States or other jurisdictions. By using the Site and providing your personal information, you acknowledge that your personal information may be transferred to and processed in jurisdictions outside your own. Please be aware that the data protection laws and regulations that may apply to your personal information transferred to the United States or other countries may be different from the laws in your country of residence. ## Information for Visitors from Australia -We are committed to handling your personal information in an open and transparent manner in accordance with applicable laws and regulations. For more information on your privacy rights, you can visit the website of The Office of the Australian Information Commissioner at [www.oaic.gov.au/](www.oaic.gov.au/). +We are committed to handling your personal information in an open and transparent manner in accordance with applicable laws and regulations. For more information on your privacy rights, you can visit the website of The Office of the Australian Information Commissioner at [https://www.oaic.gov.au/](https://www.oaic.gov.au/). ## Information for Visitors from the European Economic Area and the United Kingdom This section provides a GDPR Notice (“Notice”) for residents of the European Economic Area (“EEA”) and United Kingdom (“UK”) regarding their respective rights under the European Union’s General Data Protection Regulation and the United Kingdom’s General Data Protection Regulation (collectively, the “GDPR”). MITRE is the data controller for personal data collected through the Site. @@ -76,7 +76,7 @@ Your personal data is processed pursuant to the following legal bases: * To analyze and improve the safety and security of our Site and services, including by implementing and enhancing security measures and safeguards and protecting against fraud, spam, and other abuses; * To maintain and improve our Site and services; and * To operate and promote MITRE’s services and provide you with information and communications about our services that are tailored to, and in accordance with, your preferences. -* You have consented to our processing of your personal data. When you consent, you may change your mind and withdraw your consent at any time by emailing us at [privacy@mitre.org](privacy@mitre.org). +* You have consented to our processing of your personal data. When you consent, you may change your mind and withdraw your consent at any time by emailing us at [privacy@mitre.org](mailto:privacy@mitre.org). ### Your Rights Under the GDPR The GDPR provides individuals with certain rights regarding their personal data. You may ask us to take the following actions: @@ -88,9 +88,9 @@ The GDPR provides individuals with certain rights regarding their personal data. * object to our use of your personal data for marketing purposes; and * object to our reliance on legitimate interests as the basis for processing your personal data. -You may submit these requests by email to [privacy@mitre.org](privacy@mitre.org). We may require specific information from you to help us verify your identity prior to processing your request. Applicable law may require or permit us to decline your request. If we decline your request, we will tell you why, subject to any legal restrictions on disclosing this information. +You may submit these requests by email to [privacy@mitre.org](mailto:privacy@mitre.org). We may require specific information from you to help us verify your identity prior to processing your request. Applicable law may require or permit us to decline your request. If we decline your request, we will tell you why, subject to any legal restrictions on disclosing this information. -If you would like to submit a complaint about our use of your personal data or our response to your request regarding your personal data, you may contact us at [privacy@mitre.org](privacy@mitre.org) or submit a complaint directly to the data protection authority in your jurisdiction. If you reside in the EEA, you can find information about your data protection authority [here](https://edpb.europa.eu/about-edpb/about-edpb/members_en). If you reside in the UK, you may file complaints with the Information Commissioner’s Office [here](https://ico.org.uk/make-a-complaint/). +If you would like to submit a complaint about our use of your personal data or our response to your request regarding your personal data, you may contact us at [privacy@mitre.org](mailto:privacy@mitre.org) or submit a complaint directly to the data protection authority in your jurisdiction. If you reside in the EEA, you can find information about your data protection authority [here](https://edpb.europa.eu/about-edpb/about-edpb/members_en). If you reside in the UK, you may file complaints with the Information Commissioner’s Office [here](https://ico.org.uk/make-a-complaint/). ### Our Retention of Your Personal Data MITRE retains your personal data for no longer than is necessary to achieve the purposes for which the personal data was collected, or as may otherwise be permitted or required under applicable law. To determine the appropriate retention period, we will consider the scope and sensitivity of the personal data; the potential risk of harm from unauthorized access to, use, or disclosure of the data; the purposes for which we process the data; whether we can achieve our purposes through other means; our business needs; and applicable legal requirements. Unless otherwise required by applicable law, at the end of the retention period, we will anonymize or securely destroy your personal data. @@ -100,7 +100,7 @@ By using this Site, you acknowledge that your personal data may be collected, tr If it is necessary for us to transfer personal data out of the EEA and the UK, we do so by using suitable data transfer mechanisms, such as the standard contractual clauses approved by the European Commission, which impose data protection obligations on parties to the transfer. ## Information for Specific Individuals -Residents of U.S. states with consumer privacy laws in effect and enforceable may contact us at [privacy@mitre.org](privacy@mitre.org) for further information about our privacy practices. +Residents of U.S. states with consumer privacy laws in effect and enforceable may contact us at [privacy@mitre.org](mailto:privacy@mitre.org) for further information about our privacy practices. ## Privacy of Children This Site is not intended for children, and we do not knowingly collect personal information from children. If we become aware that we have collected personal information from a child, we will delete it in accordance with applicable law. @@ -109,24 +109,22 @@ This Site is not intended for children, and we do not knowingly collect personal MITRE may update or modify this Privacy Policy from time to time at our discretion. We will indicate changes to this Privacy Policy by updating the “Effective Date” at the beginning of the Privacy Policy. Please review this Privacy Policy periodically and especially before you provide any personal information to us. Your continued use of this Site after any update to this Privacy Policy will constitute your acceptance of our changes. ## Questions -If you have questions about this Privacy Policy or MITRE’s privacy practices, you may email [privacy@mitre.org](privacy@mitre.org). +If you have questions about this Privacy Policy or MITRE’s privacy practices, you may email [privacy@mitre.org](mailto:privacy@mitre.org). MITRE’s Data Protection Officer for Singapore may be contacted as follows: -In the United States - -Dena Kozanas – Data Protection Officer\ -Associate General Counsel & Chief Privacy Official\ -7515 Colshire Drive\ -McLean, VA 22102\ -Phone: +1 (703) 269-8515\ -Email: privacy@mitre.org - -In Singapore - -MITRE Asia Pacific Singapore\ -Thomas (Tass) Bruce Hudak – Privacy Coordinator\ -1 Changi Business Park Avenue 1\ -Suite #02-03/04\ -Singapore 486058\ -Phone: +65 8876 4609\ +In the United States
+Dena Kozanas – Data Protection Officer
+Associate General Counsel & Chief Privacy Official
+7515 Colshire Drive
+McLean, VA 22102
+Phone: +1 (703) 269-8515
+Email: [privacy@mitre.org](mailto:privacy@mitre.org) + +In Singapore
+MITRE Asia Pacific Singapore
+Thomas (Tass) Bruce Hudak – Privacy Coordinator
+1 Changi Business Park Avenue 1
+Suite #02-03/04
+Singapore 486058
+Phone: +65 8876 4609
Email: [privacy@mitre.org](mailto: privacy@mitre.org) \ No newline at end of file From 0874ec2c107e90904aad3a7c1c894d4610d7f57e Mon Sep 17 00:00:00 2001 From: Evan Lucchesi Leon <189633144+elucchesileon@users.noreply.github.com> Date: Fri, 30 May 2025 10:56:26 -0400 Subject: [PATCH 05/11] chore: fix more bad code smells and sonarqube warnings. --- modules/tests/linkchecker.py | 37 ++++++++++++++++-------------------- modules/versions/versions.py | 17 +++++++++-------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/modules/tests/linkchecker.py b/modules/tests/linkchecker.py index 717574f177d..9e2a7f1f8fb 100644 --- a/modules/tests/linkchecker.py +++ b/modules/tests/linkchecker.py @@ -1,12 +1,11 @@ import os import re -from pathlib import Path import requests -from loguru import logger -from modules import site_config import modules +from modules import site_config + from . import tests_config # STATIC PROPERTIES @@ -39,7 +38,7 @@ def remove_extra_from_path(filepath): - """Given a path, remove unwanted path from a website link""" + """Given a path, remove unwanted path from a website link.""" return filepath.split(site_config.parent_web_directory)[1] @@ -72,7 +71,7 @@ def get_correct_link(path): "docx", "rtf", ]: - if re.search(r"(css|js)\?[\w\d]+", sort_of_extension): + if re.search(r"(css|js)\?\w+", sort_of_extension): # CSS & JavaScript: check for cache-disabling query string suffix, e.g style.min.css?f8be4c06 path = path.split("?")[0] # remove suffix else: @@ -93,7 +92,7 @@ def check_if_link_in_use(filepath, link): If not, verify that the link is not the same as the filepath and add it to the in use links map. """ - if not "previous" in link and not "versions" in link: + if "previous" not in link and "versions" not in link: if not in_use_links.get(link): new_file_name = remove_extra_from_path(filepath) @@ -104,19 +103,15 @@ def check_if_link_in_use(filepath, link): in_use_links[link] = True -def remove_subdirectory_from_web_directory(): - if site_config.subdirectory: - return site_config.web_directory.split(site_config.subdirectory)[0] - else: - return site_config.web_directory - - def internal_link_test(link): """Given a link, make sure that that it exists on the file system.""" + if site_config.subdirectory: + web_dir = site_config.web_directory.split(site_config.subdirectory)[0] + else: + web_dir = site_config.web_directory + # Get correct link path - path = get_correct_link(link) - - path = remove_subdirectory_from_web_directory() + path + path = web_dir + get_correct_link(link) # e.g: contacts.html -> contacts/index.html to_index_path = path @@ -171,7 +166,7 @@ def internal_external_link_checker(filepath, html_str): # Add to relative links list if relative if is_relative: - if not link in relative_links: + if link not in relative_links: relative_links.append(link) # Get correct path @@ -228,7 +223,7 @@ def internal_link_checker(filepath, html_str): # Add to relative links list if relative if is_relative: - if not link in relative_links: + if link not in relative_links: relative_links.append(link) # Get correct path @@ -274,7 +269,7 @@ def check_unlinked_pages(filenames): """ unlinked_pages = [] for filename in filenames: - if not "previous" in filename and not "versions" in filename: + if "previous" not in filename and "versions" not in filename: # Check if it is deprecated if check_if_file_is_deprecated(filename): continue @@ -323,7 +318,7 @@ def check_links_on_page(filepath, check_external_links=False): with open(filepath, mode="r", encoding="utf8") as html: html_str = html.read() - if not "previous" in filepath and not "versions" in filepath: + if "previous" not in filepath and "versions" not in filepath: # Add redirects to in-use to avoid false positives if html_str.startswith('[Ll]ive [Vv]ersion<\/a>" - to_str = rf'href="/\g<1>"\g<2>>Live Version' + to_str = r'href="/\g<1>"\g<2>>Live Version' html_str = re.sub(from_str, to_str, html_str) # remove banner message if it is present @@ -303,7 +303,8 @@ def substitute_redirection(prefix, html_str): def build_alias(version, alias): - """build redirects from alias to version + """Build redirects from alias to version. + version is the path of the version, e.g "v5" alias is the alias to build, e.g "october2018" """ @@ -329,7 +330,7 @@ def build_alias(version, alias): def build_markdown(versions): - """build markdown for the versions list page""" + """Build markdown for the versions list page.""" # build urls versions["current"]["url"] = versionPath(versions["current"]) versions["current"]["changelog_label"] = " ".join(versions["current"]["changelog"].split("-")[1:]).title() From ac183667a597514324611e530cde8192c31d1c35 Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 3 Jun 2025 13:55:49 -0400 Subject: [PATCH 06/11] add another benefactor --- .../static/images/benefactors/BoA.png | Bin 0 -> 15344 bytes .../benefactors/templates/benefactors.html | 24 ++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 attack-theme/static/images/benefactors/BoA.png diff --git a/attack-theme/static/images/benefactors/BoA.png b/attack-theme/static/images/benefactors/BoA.png new file mode 100644 index 0000000000000000000000000000000000000000..a311231ec836979fc832db6c74fdbde6959884e6 GIT binary patch literal 15344 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 DG9Q-F literal 0 HcmV?d00001 diff --git a/modules/benefactors/templates/benefactors.html b/modules/benefactors/templates/benefactors.html index f34d0a81c11..f2e1cbf5a48 100644 --- a/modules/benefactors/templates/benefactors.html +++ b/modules/benefactors/templates/benefactors.html @@ -104,6 +104,11 @@

Supporters

logo: "/theme/images/benefactors/AttackIQ-logo.png", url: "https://attackiq.com/", title: "AttackIQ, Inc." + }, + { + logo: "/theme/images/benefactors/BoA.png", + url: "https://www.bankofamerica.com/", + title: "Bank of America, N.A." }, { logo: "/theme/images/benefactors/citi.png", @@ -114,16 +119,16 @@

Supporters

logo: "/theme/images/benefactors/crowdstrike.png", url: "https://www.crowdstrike.com/", title: "CrowdStrike, Inc." - }, + } + ], + }, + { + sponsors: [ { logo: "/theme/images/benefactors/fortinet.png", url: "https://www.fortinet.com/", title: "Fortinet" }, - ], - }, - { - sponsors: [ { logo: "/theme/images/benefactors/hca.png", url: "https://hcahealthcare.com/", @@ -139,16 +144,15 @@

Supporters

url: "https://www.lloydsbankinggroup.com/", title: "Lloyds Banking Group" }, + ], + }, + { + sponsors: [ { logo: "/theme/images/benefactors//microsoft.png", url: "https://www.microsoft.com/en-us/", title: "Microsoft Corporation" }, - - ], - }, - { - sponsors: [ { logo: "/theme/images/benefactors/verizon.png", url: "https://www.verizon.com/business/", From 481d9ac2482397a468ad9d0f4ef4eeac16f1699b Mon Sep 17 00:00:00 2001 From: adpare Date: Mon, 9 Jun 2025 16:34:36 -0400 Subject: [PATCH 07/11] update osano privacy requirements --- .../templates/general/base-template.html | 5 +- .../resources/static_pages/privacy-policy.md | 429 +++++++++++++++--- modules/site_config.py | 2 + modules/website_build/website_build.py | 5 + update-attack.py | 5 + 5 files changed, 377 insertions(+), 69 deletions(-) diff --git a/attack-theme/templates/general/base-template.html b/attack-theme/templates/general/base-template.html index 02a06001e1d..61d60899d28 100644 --- a/attack-theme/templates/general/base-template.html +++ b/attack-theme/templates/general/base-template.html @@ -23,10 +23,7 @@ {% block head %} - - + {% include "general/osano.html" %} {% include "general/google-analytics.html" %} {% include "general/google-site-verification.html" %} diff --git a/modules/resources/static_pages/privacy-policy.md b/modules/resources/static_pages/privacy-policy.md index 141a27e691c..c73ab7de931 100644 --- a/modules/resources/static_pages/privacy-policy.md +++ b/modules/resources/static_pages/privacy-policy.md @@ -6,125 +6,424 @@ Authors: Adam Pennington url: /resources/legal-and-branding/policy save_as: resources/legal-and-branding/privacy/index.html -**Effective Date: 1 July 2021** +**THE MITRE CORPORATION RESPECTS THE PRIVACY OF ITS WEBSITE USERS.** -This Privacy Policy explains the types of personal information that The MITRE Corporation (“MITRE ,” “we,” “our,” “us”) collects from visitors to the ATT&CK® website (the “Site”) at [https://attack.mitre.org](https://attack.mitre.org); how MITRE uses, shares, protects, stores, and otherwise processes that personal information; and your choices with respect to our use of your personal information. By using our Site, you acknowledge that you understand and agree to the terms outlined in this Privacy Policy. If you have any questions, you may contact us using the information provided at the end of this Privacy Policy. +**Effective Date: [XX/XX/2025]** +This Privacy Policy explains the types of personal information that The +MITRE Corporation ("**MITRE** ," "**we**," "**our**," "**us**") collects +from visitors to the MITRE ATT&CK® website (the "**Site**") at +https://attack.mitre.org; how MITRE uses, shares, protects, stores, and +otherwise processes that personal information; and your choices with +respect to our use of your personal information. By using our Site, you +acknowledge that you understand and agree to the terms outlined in this +Privacy Policy. If you have any questions, you may contact us using the +information provided at the end of this Privacy Policy. + +This notice is provided in a layered format so you can click through to +the specific areas listed below. + +- [Personal Information We + Collect](#personal-information-we-collect) + +- [How We Use Personal + Information](#how-we-use-personal-information) + +- [How We Share Personal + Information](#how-we-share-personal-information) + +- [Linked Websites](#linked-websites) + +- [Security of Personal + Information](#security-of-personal-information) + +- [Information For Visitors from Outside the United + States](#information-for-visitors-from-outside-the-united-states) + +- [Information for Visitors from + Australia](#information-for-visitors-from-australia) + +- [Information for Visitors from the European Economic Area and the + United + Kingdom](#information-for-visitors-from-the-european-economic-area-and-the-united-kingdom) + +- [Information for Specific + Individuals](#information-for-specific-individuals) + +- [Privacy of Children](#privacy-of-children) + +- [Changes to Our Privacy + Policy](#changes-to-our-privacy-policy) + +- [Questions](#questions) + +
+

+
## Personal Information We Collect + ### Personal Information You Give Us -MITRE may obtain your personal information when you interact with our Site, for example, when you request information using the “Contact Us” link. Personal information is data that identifies you, or could reasonably be used to identify you, as an individual, such as your name, postal address, email address, and phone number. + +MITRE may obtain your personal information when you interact with our +Site, for example, when you request information using the "**Contact +Us**" link. Personal information is data that identifies you, or could +reasonably be used to identify you, as an individual, such as your name +and email address. ### Information We Collect Automatically -We also may collect other information about your visits to our Site using automated tools; for example, cookies and other passive information collection technologies enable MITRE to compile aggregate statistics concerning use of the Site, analyze trends, enhance the security of the Site, deliver content, and otherwise administer and improve the Site. This information may include your browser type, language preference, operating system, device identifier, device type, access time, Internet Protocol (IP) address, the URLs of websites you visited before and after visiting our Site, the web search that landed you on our Site, length of your visits to our Site, and the links you click and pages you visit within our Site. -Your web browser may have settings that allow you to transmit a “Do Not Track” signal when you visit various websites or use online services. Like many websites, our Site is not designed to respond to “Do Not Track” signals received from browsers. To learn more about online tracking, the Federal Trade Commission (FTC) provides guidance on [How To Protect Your Privacy Online](https://www.consumer.ftc.gov/articles/how-protect-your-privacy-online). -The MITRE ATT&CK website is hosted on GitHub®, which provides internet hosting for software development and version control. When GitHub is visited, the visitor's IP address is logged and stored for security purposes, regardless of whether the visitor has signed into GitHub or not. For more information about GitHub's privacy and security practices, see the [GitHub Privacy Statement](https://docs.github.com/en/github/site-policy/github-privacy-statement). MITRE and GitHub use a free third-party software service called Google Analytics® to help us understand and analyze how visitors use our Site. For more information on how Google Analytics uses data collected through the Site, visit [https://www.google.com/policies/privacy/partners/](https://www.google.com/policies/privacy/partners/). To opt out of Google Analytics cookies, visit: [https://www.google.com/settings/ads](https://www.google.com/settings/ads) and [https://tools.google.com/dlpage/gaoptout/](https://tools.google.com/dlpage/gaoptout/). +We also may collect other information about your visits to our Site +using automated tools; for example, cookies and other passive +information collection technologies enable MITRE to compile aggregate +statistics concerning use of the Site, analyze trends, enhance the +security of the Site, deliver content, and otherwise administer +and improve the Site. This information may include your browser type, +language preference, operating system, device identifier, device type, +access time, Internet Protocol (IP) address, the URLs of websites you +visited before and after visiting our Site, the web search that landed +you on our Site, length of your visits to our Site, and the links you +click and pages you visit within our Site. + +Your web browser may have settings that allow you to transmit a "**Do +Not Track**" signal when you visit various websites or use online +services. Like many websites, our Site is not designed to respond to +"**Do Not Track**" signals received from browsers. To learn more about +online tracking, the Federal Trade Commission (FTC) provides guidance on +[How To Protect Your Privacy +Online](https://www.consumer.ftc.gov/articles/how-protect-your-privacy-online). +The ATT&CK website is hosted on GitHub®, which provides internet hosting +for software development and version control. When GitHub is visited, +the visitor\'s IP address is logged and stored for security purposes, +regardless of whether the visitor has signed into GitHub or not. For +more information about GitHub\'s privacy and security practices, see the +[GitHub Privacy +Statement](https://docs.github.com/en/github/site-policy/github-privacy-statement). +MITRE and GitHub use a free third-party software service called Google +Analytics® to help us understand and analyze how visitors use our Site. +For more information on how Google Analytics uses data collected through +the Site, visit . To +opt out of Google Analytics cookies, visit: + and +. + +
+

+
## How We Use Personal Information + MITRE may use personal information we collect through our Site to: -* communicate with you, including to respond to your questions and requests, send you notices about our services, or contact you for additional information when needed; -* analyze Site trends, usage, and the activities of Site visitors; -* improve our Site and notify you about important updates; -* perform internal business analyses or for other business purposes consistent with our mission; -* facilitate, manage, personalize, and improve our partnership relationships; -* identify, prevent, investigate, and take other actions with respect to suspected or actual fraud or illegal activity or other activity that violates our policies; -* ensure the security and integrity of our personal information processing; -* comply with applicable laws, rules, regulations, and legal processes as well as our company policies; and -* fulfill other purposes, with your consent (as required). +- communicate with you, including to respond to your questions and + requests, send you notices about our services, or contact you for + additional information when needed; + +- analyze Site trends, usage, and the activities of Site visitors; + +- improve our Site and notify you about important updates; + +- perform internal business analyses or for other business purposes + consistent with our mission; + +- facilitate, manage, personalize, and improve our partnership + relationships; + +- identify, prevent, investigate, and take other actions with respect + to suspected or actual fraud or illegal activity or other activity + that violates our policies; + +- ensure the security and integrity of our personal information + processing; + +- comply with applicable laws, rules, regulations, and legal processes + as well as our company policies; and + +- fulfill other purposes, with your consent (as required). + +
+

+
## How We Share Personal Information + MITRE may share your personal information within our organization to: -* better respond to your inquiries; -* perform marketing research and for sales, support, and service-related purposes; -* protect rights, property, life, health, security, and safety; -* negotiate or complete any proposed or actual merger, purchase, sale, or any other type of acquisition or other transaction, including a transfer of all or a portion of our business to another organization; -* disclose personal information with your consent or at your direction; and -* achieve any other purpose consistent with our statements in this Privacy Policy or otherwise allowed by applicable law. -MITRE may disclose your personal information to comply with applicable law, such as in response to requests from law enforcement agencies, regulators, other public authorities, courts, and third-party litigants in connection with legal proceedings or investigations. +- better respond to your inquiries; + +- perform marketing research and for sales, support, and + service-related purposes; + +- protect rights, property, life, health, security, and safety; + +- negotiate or complete any proposed or actual merger, purchase, sale, + or any other type of acquisition or other transaction, including a + transfer of all or a portion of our business to another + organization; + +- disclose personal information with your consent or at your + direction; and + +- achieve any other purpose consistent with our statements in this + Privacy Policy or otherwise allowed by applicable law. +MITRE may disclose your personal information to comply with applicable +law, such as in response to requests from law enforcement agencies, +regulators, other public authorities, courts, and third-party litigants +in connection with legal proceedings or investigations. + +
+

+
## Linked Websites -Our Site may include links to other websites that are not owned or operated by MITRE. This Privacy Policy does not apply to those websites, which may have their own privacy policies that you should review to understand how they may collect, use, or disclose your personal information. MITRE is not responsible for the content or privacy practices of any linked websites that it does not control. +Our Site may include links to other websites that are not owned or +operated by MITRE. This Privacy Policy does not apply to those websites, +which may have their own privacy policies that you should review to +understand how they may collect, use, or disclose your personal +information. MITRE is not responsible for the content or privacy +practices of any linked websites that it does not control. + +
+

+
## Security of Personal Information -MITRE maintains reasonable safeguards designed to protect personal information from loss, theft, misuse, and unauthorized access, disclosure, alteration, and destruction. MITRE employs encryption technologies and user authentication procedures that are designed to keep data secure. Nevertheless, transmission via the Internet and online digital storage are not completely secure, so we cannot guarantee the security of your personal information. +MITRE maintains reasonable safeguards designed to protect personal +information from loss, theft, misuse, and unauthorized access, +disclosure, alteration, and destruction. MITRE employs encryption +technologies and user authentication procedures that are designed to +keep data secure. Nevertheless, transmission via the Internet and online +digital storage are not completely secure, so we cannot guarantee the +security of your personal information. + +
+

+
## Information for Visitors from Outside the United States -MITRE is based in the United States. If you are visiting our Site from outside the United States, please be aware that information we obtain about you may be transferred to and processed in the United States or other jurisdictions. By using the Site and providing your personal information, you acknowledge that your personal information may be transferred to and processed in jurisdictions outside your own. Please be aware that the data protection laws and regulations that may apply to your personal information transferred to the United States or other countries may be different from the laws in your country of residence. +MITRE is based in the United States. If you are visiting our Site from +outside the United States, please be aware that information we obtain +about you may be transferred to and processed in the United States or +other jurisdictions. By using the Site and providing your personal +information, you acknowledge that your personal information may be +transferred to and processed in jurisdictions outside your own. Please +be aware that the data protection laws and regulations that may apply to +your personal information transferred to the United States or other +countries may be different from the laws in your country of residence. + +
+

+
## Information for Visitors from Australia -We are committed to handling your personal information in an open and transparent manner in accordance with applicable laws and regulations. For more information on your privacy rights, you can visit the website of The Office of the Australian Information Commissioner at [https://www.oaic.gov.au/](https://www.oaic.gov.au/). +We are committed to handling your personal information in an open and +transparent manner in accordance with applicable laws and regulations. +For more information on your privacy rights, you can visit the website +of The Office of the Australian Information Commissioner at +[www.oaic.gov.au/](http://www.oaic.gov.au/). + +
+

+
## Information for Visitors from the European Economic Area and the United Kingdom -This section provides a GDPR Notice (“Notice”) for residents of the European Economic Area (“EEA”) and United Kingdom (“UK”) regarding their respective rights under the European Union’s General Data Protection Regulation and the United Kingdom’s General Data Protection Regulation (collectively, the “GDPR”). MITRE is the data controller for personal data collected through the Site. -This Notice supplements the information in this Privacy Policy and other MITRE privacy policies and notices. If there is a conflict between any other MITRE privacy policy, statement, or notice and this Notice, this Notice will prevail. +This section provides a GDPR Notice ("**Notice**") for residents of the +European Economic Area ("**EEA**") and United Kingdom ("**UK**") +regarding their respective rights under the European Union's General +Data Protection Regulation and the United Kingdom's General Data +Protection Regulation (collectively, the "**GDPR**"). MITRE is the data +controller for personal data collected through the Site. + +This Notice supplements the information in this Privacy Policy and other +MITRE privacy policies and notices. If there is a conflict between any +other MITRE privacy policy, statement, or notice and this Notice, this +Notice will prevail. ### Our Collection and Use of Personal Data + Personal data collected through this Site may include: -* Contact Data. You may provide your contact details, such as your name, phone number, postal address, email address, and company affiliation; for example, when you contact us for further information or subscribe to receive our news and information offerings. -* Device Data. We may obtain information about devices that access our Site, including the type of device, operating system, device settings, unique device identifiers, and error data. -* Other Data You Provide. This includes personal data you include in communications you send to us, such as inquiries about our services. + +- **Contact Data**. You may provide your contact details, such as your + name, phone number, postal address, email address, and company + affiliation; for example, when you contact us for further + information or subscribe to receive our news and information + offerings. + +- **Device Data**. We may obtain information about devices that access + our Site, including the type of device, operating system, device + settings, unique device identifiers, and error data. + +- **Other Data You Provide**. This includes personal data you include + in communications you send to us, such as inquiries about our + services. ### Our Processing of Your Personal Data -Your personal data is required for us to provide some of our services. In some instances, if you fail to provide your personal data, you may not be able to access or use our services. We may process the personal data you provide for any of the purposes identified in the “How We Use Personal Information” and “How We Share Personal Information” Sections of this Privacy Policy. -Your personal data is processed pursuant to the following legal bases: -* The processing is necessary for us to provide you with the services you request or to respond to your questions. -* We have a legal obligation to process your personal data, such as compliance with applicable tax laws or other government regulations or compliance with a court order or binding law enforcement request. -* We have a legitimate interest in processing your personal data and our reasons for using the personal data outweigh the potential prejudice to your data protection rights. In particular, we have a legitimate interest in the following instances: - * To analyze and improve the safety and security of our Site and services, including by implementing and enhancing security measures and safeguards and protecting against fraud, spam, and other abuses; - * To maintain and improve our Site and services; and - * To operate and promote MITRE’s services and provide you with information and communications about our services that are tailored to, and in accordance with, your preferences. -* You have consented to our processing of your personal data. When you consent, you may change your mind and withdraw your consent at any time by emailing us at [privacy@mitre.org](mailto:privacy@mitre.org). +Your personal data is required for us to provide some of our services. +In some instances, if you fail to provide your personal data, you may +not be able to access or use our services. We may process the personal +data you provide for any of the purposes identified in the "**How We Use +Personal Information**" and "**How We Share Personal Information**" +Sections of this Privacy Policy. + +Your personal data is processed pursuant to the following **legal +bases**: + +- The processing is **necessary for us to provide you with the + services** you request or to respond to your questions. + +- We have a **legal obligation** to process your personal data, such + as compliance with applicable tax laws or other government + regulations or compliance with a court order or binding law + enforcement request. + +- We have a **legitimate interest** in processing your personal data + and our reasons for using the personal data outweigh the potential + prejudice to your data protection rights. In particular, we have a + legitimate interest in the following instances: + + - To analyze and improve the safety and security of our Site and + services, including by implementing and enhancing security + measures and safeguards and protecting against fraud, spam, and + other abuses; + + - To maintain and improve our Site and services; and + + - To operate and promote MITRE's services and provide you with + information and communications about our services that are + tailored to, and in accordance with, your preferences. + +- You have **consented to** our processing of your personal data. When + you consent, you may change your mind and withdraw your consent at + any time by emailing us at + [privacy@mitre.org](mailto:Privacy@mitre..org). ### Your Rights Under the GDPR -The GDPR provides individuals with certain rights regarding their personal data. You may ask us to take the following actions: -* provide you with information about our processing of your personal data and access to your personal data; -* update or correct inaccuracies in your personal data; -* delete your personal data; -* transfer a copy of your personal data to you or a third party of your choice; -* restrict the processing of your personal data; -* object to our use of your personal data for marketing purposes; and -* object to our reliance on legitimate interests as the basis for processing your personal data. -You may submit these requests by email to [privacy@mitre.org](mailto:privacy@mitre.org). We may require specific information from you to help us verify your identity prior to processing your request. Applicable law may require or permit us to decline your request. If we decline your request, we will tell you why, subject to any legal restrictions on disclosing this information. +The GDPR provides individuals with certain rights regarding their +personal data. You may ask us to take the following actions: + +- provide you with information about our processing of your personal + data and access to your personal data; + +- update or correct inaccuracies in your personal data; + +- delete your personal data; + +- transfer a copy of your personal data to you or a third party of + your choice; + +- restrict the processing of your personal data; + +- object to our use of your personal data for marketing purposes; and + +- object to our reliance on legitimate interests as the basis for + processing your personal data. + +You may submit these requests by email to +[privacy@mitre.org](mailto:Privacy@MITRE.org). We may +require specific information from you to help us verify your identity +prior to processing your request. Applicable law may require or permit +us to decline your request. If we decline your request, we will tell you +why, subject to any legal restrictions on disclosing this information. -If you would like to submit a complaint about our use of your personal data or our response to your request regarding your personal data, you may contact us at [privacy@mitre.org](mailto:privacy@mitre.org) or submit a complaint directly to the data protection authority in your jurisdiction. If you reside in the EEA, you can find information about your data protection authority [here](https://edpb.europa.eu/about-edpb/about-edpb/members_en). If you reside in the UK, you may file complaints with the Information Commissioner’s Office [here](https://ico.org.uk/make-a-complaint/). +If you would like to submit a complaint about our use of your personal +data or our response to your request regarding your personal data, you +may contact us at +[privacy@mitre.org](mailto:privacy@mitre.org) or submit a +complaint directly to the data protection authority in your +jurisdiction. If you reside in the EEA, you can find information about +your data protection authority +[here](https://edpb.europa.eu/about-edpb/about-edpb/members_en). +If you reside in the UK, you may file complaints with the Information +Commissioner's Office +[here](https://ico.org.uk/make-a-complaint/). ### Our Retention of Your Personal Data -MITRE retains your personal data for no longer than is necessary to achieve the purposes for which the personal data was collected, or as may otherwise be permitted or required under applicable law. To determine the appropriate retention period, we will consider the scope and sensitivity of the personal data; the potential risk of harm from unauthorized access to, use, or disclosure of the data; the purposes for which we process the data; whether we can achieve our purposes through other means; our business needs; and applicable legal requirements. Unless otherwise required by applicable law, at the end of the retention period, we will anonymize or securely destroy your personal data. + +MITRE retains your personal data for no longer than is necessary to +achieve the purposes for which the personal data was collected, or as +may otherwise be permitted or required under applicable law. To +determine the appropriate retention period, we will consider the scope +and sensitivity of the personal data; the potential risk of harm from +unauthorized access to, use, or disclosure of the data; the purposes for +which we process the data; whether we can achieve our purposes through +other means; our business needs; and applicable legal requirements. +Unless otherwise required by applicable law, at the end of the retention +period, we will anonymize or securely destroy your personal data. ### Personal Data Transfers -By using this Site, you acknowledge that your personal data may be collected, transferred to, and processed in jurisdictions outside your own. When you directly provide your personal data through our Site, you acknowledge that your personal data is being provided by you to a company based in the United States. The laws that apply to personal data protection in the United States differ from those applicable in the EEA and the UK. -If it is necessary for us to transfer personal data out of the EEA and the UK, we do so by using suitable data transfer mechanisms, such as the standard contractual clauses approved by the European Commission, which impose data protection obligations on parties to the transfer. -## Information for Specific Individuals -Residents of U.S. states with consumer privacy laws in effect and enforceable may contact us at [privacy@mitre.org](mailto:privacy@mitre.org) for further information about our privacy practices. +By using this Site, you acknowledge that your personal data may be +collected, transferred to, and processed in jurisdictions outside your +own. When you directly provide your personal data through our Site, you +acknowledge that your personal data is being provided by you to a +company based in the United States. The laws that apply to personal data +protection in the United States differ from those applicable in the EEA +and the UK. + +If it is necessary for us to transfer personal data out of the EEA and +the UK, we do so by using suitable data transfer mechanisms, such as the +standard contractual clauses approved by the European Commission, which +impose data protection obligations on parties to the transfer. + +
+

+
+## Information for Specific Individuals + +Residents of U.S. states with consumer privacy laws in effect and +enforceable may contact us at +[privacy@mitre.org](mailto:privacy@mitre.org) for further +information about our privacy practices. +
+

+
## Privacy of Children -This Site is not intended for children, and we do not knowingly collect personal information from children. If we become aware that we have collected personal information from a child, we will delete it in accordance with applicable law. +This Site is not intended for children, and we do not knowingly collect +personal information from children. If we become aware that we have +collected personal information from a child, we will delete it in +accordance with applicable law. + +
+

+
## Changes to Our Privacy Policy -MITRE may update or modify this Privacy Policy from time to time at our discretion. We will indicate changes to this Privacy Policy by updating the “Effective Date” at the beginning of the Privacy Policy. Please review this Privacy Policy periodically and especially before you provide any personal information to us. Your continued use of this Site after any update to this Privacy Policy will constitute your acceptance of our changes. +MITRE may update or modify this Privacy Policy from time to time at our +discretion. We will indicate changes to this Privacy Policy by updating +the "**Effective Date**" at the beginning of the Privacy Policy. Please +review this Privacy Policy periodically and especially before you +provide any personal information to us. Your continued use of this Site +after any update to this Privacy Policy will constitute your acceptance +of our changes. + +
+

+
## Questions -If you have questions about this Privacy Policy or MITRE’s privacy practices, you may email [privacy@mitre.org](mailto:privacy@mitre.org). -MITRE’s Data Protection Officer for Singapore may be contacted as follows: -In the United States
-Dena Kozanas – Data Protection Officer
+If you have questions about this Privacy Policy or MITRE's privacy +practices, you may email +[privacy@mitre.org](mailto:privacy@mitre.org). + +MITRE's Data Protection Officer for Singapore may be contacted as +follows: + +**In the United States**
+Dena Kozanas -- *Data Protection Officer *
Associate General Counsel & Chief Privacy Official
7515 Colshire Drive
McLean, VA 22102
Phone: +1 (703) 269-8515
-Email: [privacy@mitre.org](mailto:privacy@mitre.org) +Email: [privacy@mitre.org](mailto:%20privacy@mitre.org)
-In Singapore
+**In Singapore **
MITRE Asia Pacific Singapore
-Thomas (Tass) Bruce Hudak – Privacy Coordinator
+Thomas (Tass) Bruce Hudak -- *Privacy Coordinator *
1 Changi Business Park Avenue 1
Suite #02-03/04
Singapore 486058
Phone: +65 8876 4609
-Email: [privacy@mitre.org](mailto: privacy@mitre.org) \ No newline at end of file +Email: [privacy@mitre.org](mailto:%20privacy@mitre.org)
diff --git a/modules/site_config.py b/modules/site_config.py index 9bb53627140..799672c2f78 100644 --- a/modules/site_config.py +++ b/modules/site_config.py @@ -191,3 +191,5 @@ def set_subdirectory(subdirectory_str): GOOGLE_ANALYTICS = os.getenv("GOOGLE_ANALYTICS") GOOGLE_SITE_VERIFICATION = os.getenv("GOOGLE_SITE_VERIFICATION") + +INCLUDE_OSANO = os.getenv("INCLUDE_OSANO") diff --git a/modules/website_build/website_build.py b/modules/website_build/website_build.py index cf58b5299b3..28ab9aeb8e8 100644 --- a/modules/website_build/website_build.py +++ b/modules/website_build/website_build.py @@ -236,17 +236,22 @@ def pelican_content(): google_analytics = site_config.GOOGLE_ANALYTICS google_site_verification = site_config.GOOGLE_SITE_VERIFICATION + include_osano = site_config.INCLUDE_OSANO if site_config.args.google_analytics: google_analytics = site_config.args.google_analytics if site_config.args.google_site_verification: google_site_verification = site_config.args.google_site_verification + if site_config.args.include_osano: + include_osano = site_config.args.include_osano extra_settings = "" if google_analytics: extra_settings = f"{extra_settings} GOOGLE_ANALYTICS='\"{google_analytics}\"'" if google_site_verification: extra_settings = f"{extra_settings} GOOGLE_SITE_VERIFICATION='\"{google_site_verification}\"'" + if include_osano: + extra_settings = f"{extra_settings} INCLUDE_OSANO='\"{include_osano}\"'" if extra_settings: pelican_cmd = f"{pelican_cmd} -e {extra_settings}" diff --git a/update-attack.py b/update-attack.py index 4b4fc048bdb..1977cf72b17 100644 --- a/update-attack.py +++ b/update-attack.py @@ -158,6 +158,11 @@ def get_parsed_args(): type=str, help=("If a Google site verification code is provided, then the site will include it on all pages."), ) + parser.add_argument( + "--include-osano", + action="store_true", + help=("If specified, the site will include the Osano privacy compliance script."), + ) args = parser.parse_args() From bb274e92ddb88015e542d7eaa3af9b89039e56bc Mon Sep 17 00:00:00 2001 From: adpare Date: Mon, 9 Jun 2025 16:34:55 -0400 Subject: [PATCH 08/11] add osano file --- attack-theme/templates/general/osano.html | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 attack-theme/templates/general/osano.html diff --git a/attack-theme/templates/general/osano.html b/attack-theme/templates/general/osano.html new file mode 100644 index 00000000000..4b91c1eead3 --- /dev/null +++ b/attack-theme/templates/general/osano.html @@ -0,0 +1,6 @@ +{% if INCLUDE_OSANO %} + + +{% endif %} From 2ac1f55e00c50feede07f09b48c8371cd27f692f Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 10 Jun 2025 00:09:16 -0400 Subject: [PATCH 09/11] fix security issues --- attack-style/package-lock.json | 536 ++++++++++++++++++--------------- attack-style/package.json | 6 + 2 files changed, 294 insertions(+), 248 deletions(-) diff --git a/attack-style/package-lock.json b/attack-style/package-lock.json index d6e38e2ee58..ea3a162e085 100644 --- a/attack-style/package-lock.json +++ b/attack-style/package-lock.json @@ -478,25 +478,49 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" } }, - "node_modules/@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "license": "MIT", "dependencies": { - "defer-to-connect": "^1.0.1" - }, - "engines": { - "node": ">=6" + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.0.tgz", + "integrity": "sha512-yZQa2zm87aRVcqDyH5+4Hv9KYgSdgwX1rFnGvpbzMaC7YAljmhBET93TPiTd3ObwTL+gSpIzPKg5BqVxdCvxKg==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/a-sync-waterfall": { @@ -728,63 +752,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT" }, - "node_modules/cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "license": "MIT", - "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cacheable-request/node_modules/json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", - "license": "MIT" - }, - "node_modules/cacheable-request/node_modules/keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.0" - } - }, - "node_modules/cacheable-request/node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -1029,6 +996,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", @@ -1102,9 +1078,9 @@ "license": "MIT" }, "node_modules/commander": { - "version": "2.17.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", - "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT" }, "node_modules/concat-map": { @@ -1287,15 +1263,30 @@ } }, "node_modules/decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "license": "MIT", "dependencies": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-extend": { @@ -1308,10 +1299,13 @@ } }, "node_modules/defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "license": "MIT" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/define-data-property": { "version": "1.1.4", @@ -1446,12 +1440,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", - "license": "BSD-3-Clause" - }, "node_modules/duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -1886,18 +1874,6 @@ "node": ">=0.10.0" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -2115,28 +2091,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "license": "MIT", - "dependencies": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2207,24 +2161,24 @@ } }, "node_modules/html-minifier": { - "version": "3.5.21", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", - "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", "license": "MIT", "dependencies": { - "camel-case": "3.0.x", - "clean-css": "4.2.x", - "commander": "2.17.x", - "he": "1.2.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" }, "bin": { "html-minifier": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/html-tags": { @@ -2241,9 +2195,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "license": "BSD-2-Clause" }, "node_modules/ignore": { @@ -2581,7 +2535,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { @@ -2617,7 +2570,6 @@ "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" @@ -2902,15 +2854,6 @@ "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", "license": "MIT" }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -2935,18 +2878,6 @@ "semver": "bin/semver.js" } }, - "node_modules/marked": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.3.tgz", - "integrity": "sha512-Fqa7eq+UaxfMriqzYLayfqAE40WN03jf+zHjT18/uXNuzjq3TY0XTbrAoPeqSJrAmPz11VuUA+kBPYOhHt9oOQ==", - "license": "MIT", - "bin": { - "marked": "bin/marked" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3017,15 +2948,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -3130,15 +3052,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", - "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/now-and-later": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", @@ -3246,15 +3159,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/package-json": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", @@ -3270,6 +3174,152 @@ "node": ">=8" } }, + "node_modules/package-json/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/package-json/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/package-json/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/package-json/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/package-json/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/package-json/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -3483,15 +3533,6 @@ "dev": true, "license": "MIT" }, - "node_modules/prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3573,6 +3614,18 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -3746,6 +3799,12 @@ "node": ">=0.10.0" } }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -3768,15 +3827,6 @@ "node": ">= 0.10" } }, - "node_modules/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", - "license": "MIT", - "dependencies": { - "lowercase-keys": "^1.0.0" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -3960,6 +4010,18 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/sass-convert/node_modules/semver-regex": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", + "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/sass-convert/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4023,6 +4085,18 @@ "marked": "^0.6.2" } }, + "node_modules/sassdoc-extras/node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/sassdoc-theme-default": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/sassdoc-theme-default/-/sassdoc-theme-default-2.8.6.tgz", @@ -4181,15 +4255,6 @@ "semver": "bin/semver.js" } }, - "node_modules/semver-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", - "integrity": "sha512-1vZcoRC+LPtHFkLUPyrabsATDSHerxW+hJBN8h04HZOZBuewbXaNROtUVdEPrTdZsWNq6sfsXDhd48GB2xTG4g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -4677,15 +4742,6 @@ "node": ">=0.10.0" } }, - "node_modules/to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4776,14 +4832,10 @@ } }, "node_modules/uglify-js": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", - "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "license": "BSD-2-Clause", - "dependencies": { - "commander": "~2.19.0", - "source-map": "~0.6.1" - }, "bin": { "uglifyjs": "bin/uglifyjs" }, @@ -4791,12 +4843,6 @@ "node": ">=0.8.0" } }, - "node_modules/uglify-js/node_modules/commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "license": "MIT" - }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -4806,6 +4852,12 @@ "node": ">=0.10.0" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, "node_modules/unique-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", @@ -4874,18 +4926,6 @@ "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", "license": "MIT" }, - "node_modules/url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", - "license": "MIT", - "dependencies": { - "prepend-http": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/attack-style/package.json b/attack-style/package.json index 0dcfc246e2b..f7c43ad7b19 100644 --- a/attack-style/package.json +++ b/attack-style/package.json @@ -23,5 +23,11 @@ "dependencies": { "sass": "^1.83.0", "sassdoc": "^2.7.4" + }, + "overrides": { + "marked": "^4.0.10", + "got": "^11.8.5", + "semver-regex": "^3.1.4", + "html-minifier": "^4.0.0" } } From f8c235f0514bc497f8844e9438e3c94124dad88f Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 10 Jun 2025 00:26:05 -0400 Subject: [PATCH 10/11] update gh-pages env --- .github/workflows/gh-pages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index fcee4882af4..6199fffc5b7 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -47,6 +47,7 @@ jobs: env: GOOGLE_ANALYTICS: ${{ secrets.GOOGLE_ANALYTICS }} GOOGLE_SITE_VERIFICATION: ${{ secrets.GOOGLE_SITE_VERIFICATION }} + INCLUDE_OSANO: true - name: Cleanup build run: rm -rf attack-versions From f17e3e8beef4acdf8f3c826913e258f84dbd75a4 Mon Sep 17 00:00:00 2001 From: adpare Date: Tue, 10 Jun 2025 12:19:07 -0400 Subject: [PATCH 11/11] update privacy policy date --- modules/resources/static_pages/privacy-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/resources/static_pages/privacy-policy.md b/modules/resources/static_pages/privacy-policy.md index c73ab7de931..ad70bf9676c 100644 --- a/modules/resources/static_pages/privacy-policy.md +++ b/modules/resources/static_pages/privacy-policy.md @@ -8,7 +8,7 @@ save_as: resources/legal-and-branding/privacy/index.html **THE MITRE CORPORATION RESPECTS THE PRIVACY OF ITS WEBSITE USERS.** -**Effective Date: [XX/XX/2025]** +**Effective Date: 10 June 2025** This Privacy Policy explains the types of personal information that The MITRE Corporation ("**MITRE** ," "**we**," "**our**," "**us**") collects