diff --git a/.gitignore b/.gitignore index a57b1721080..848e08e2e49 100644 --- a/.gitignore +++ b/.gitignore @@ -15,10 +15,11 @@ output/ reports/ attack-theme/templates/general/base.html -attack-theme/templates/contribute +attack-theme/templates/benefactors attack-theme/templates/datasources attack-theme/templates/groups attack-theme/templates/campaigns +attack-theme/templates/assets attack-theme/templates/matrices attack-theme/templates/mitigations attack-theme/templates/resources @@ -28,6 +29,7 @@ attack-theme/templates/techniques attack-theme/templates/website_build attack-theme/templates/versions attack-theme/static/scripts/settings.js +attack-theme/templates/general/sidebar-resources.html content/ data/pelican_settings.json diff --git a/attack-search/src/settings.js b/attack-search/src/settings.js index db171b6711a..a01ad11ff3e 100644 --- a/attack-search/src/settings.js +++ b/attack-search/src/settings.js @@ -2,6 +2,7 @@ const baseURL = ''; // TODO migrate from base_url (generated via Pelican) const searchFilePaths = [ "campaigns.json", + "assets.json", "datasources.json", "groups.json", "matrices.json", diff --git a/attack-theme/static/scripts/mobileview-datasources.js b/attack-theme/static/scripts/mobileview-datasources.js new file mode 100644 index 00000000000..10020cae820 --- /dev/null +++ b/attack-theme/static/scripts/mobileview-datasources.js @@ -0,0 +1,16 @@ +// This code is for creating a collapsable sidebar for the mobile view +let mediaQuery = window.matchMedia('(max-width: 47.9875rem)') + +function mobileSidenav(e) { + if (e.matches) { + $('#sidebar-collapse').collapse('hide') + } + else{ + $('#sidebar-collapse').collapse('show') + } +} +$(document).ready(function() { + mobileSidenav(mediaQuery) +}); + +mediaQuery.addEventListener('change', mobileSidenav) \ No newline at end of file diff --git a/attack-theme/static/scripts/resizer.js b/attack-theme/static/scripts/resizer.js index eb6b3972bf1..da812317539 100644 --- a/attack-theme/static/scripts/resizer.js +++ b/attack-theme/static/scripts/resizer.js @@ -31,21 +31,4 @@ function resizeSidebar_mouseupHandler() { } resizer.addEventListener("mousedown", resizeSidebar_mousedownHandler); -} - -//This code is for creating a collapsable sidebar for the mobile view -const mediaQuery = window.matchMedia('(max-width: 47.9875rem)') - -function mobileSidenav(e) { - if (e.matches) { - $('#sidebar-collapse').collapse('hide') - } - else{ - $('#sidebar-collapse').collapse('show') - } -} -$(document).ready(function() { - mobileSidenav(mediaQuery) -}); - -mediaQuery.addEventListener('change', mobileSidenav) \ No newline at end of file +} \ No newline at end of file diff --git a/attack-theme/static/scripts/sidebar-load-all.js b/attack-theme/static/scripts/sidebar-load-all.js new file mode 100644 index 00000000000..e92a5e28a58 --- /dev/null +++ b/attack-theme/static/scripts/sidebar-load-all.js @@ -0,0 +1,52 @@ +let mod_name = window.location.pathname.split("/") +let mod_entry = "/" + mod_name[1] + "/sidebar-" + mod_name[1] +if (mod_name.includes('contact')){ + mod_entry = "/" + "resources/sidebar-resources" +} +$("#sidebars").load(mod_entry, function() { + let navElements = document.querySelectorAll('.sidenav-head > a'); + let winlocation; + navElements.forEach(function(element){ + if(!element.href.includes('changelog.html')){ + if(!window.location.href.endsWith("/")){ + winlocation = window.location.href + "/"; + } + else{ + winlocation = window.location.href + } + if(!element.href.endsWith("/")){ + element.href = element.href + "/"; + } + } + else{ + winlocation = window.location.href + } + if(element.href == winlocation){ + $(element.parentNode).addClass("active") + }}); + + //This code is for creating a collapsable sidebar for the mobile view + let mediaQuery = window.matchMedia('(max-width: 47.9875rem)') + function mobileSidenav(e) { + if (e.matches) { + $('#sidebar-collapse').collapse('hide') + } + else{ + $('#sidebar-collapse').collapse('show') + } + } + $(document).ready(function() { + mobileSidenav(mediaQuery) + let sidenav = $(".sidenav-list"); + let sidenav_active_elements = $(".sidenav .active"); + if (sidenav_active_elements.length > 0) setTimeout(() => { //setTimeout gives bootstrap time to execute first + let offsetValue = sidenav_active_elements[0].offsetTop; + if (offsetValue <= 0){ + offsetValue = sidenav_active_elements[sidenav_active_elements.length - 1].offsetTop; + } + sidenav[0].scrollTop = offsetValue - 60; + }); + }); + + mediaQuery.addEventListener('change', mobileSidenav) +}); \ No newline at end of file diff --git a/attack-theme/static/style/_layouts.scss b/attack-theme/static/style/_layouts.scss index aeabc3ea157..a95419338e0 100644 --- a/attack-theme/static/style/_layouts.scss +++ b/attack-theme/static/style/_layouts.scss @@ -388,6 +388,10 @@ a { } } +.tables-mobile { + overflow-y: auto; +} + .table-bordered, .blog-post table, .changelog table { &, & td, & th { border: 1px solid border-color(body); @@ -1051,6 +1055,11 @@ pre { /******/ +// how to display the sidebar +div#sidebars { + display: contents +} + /*Plus/Minus expand icons*/ // used in the expandable list items in the side navigation and in the expandable gray blocks in ATT&CKCON .expand-icon { @@ -1137,6 +1146,10 @@ pre { } } +.contact-center { + margin-block-start: 20vh; +} + /*Card Blocks*/ // container of groups of cards .expand-panel { diff --git a/attack-theme/templates/general/attackcon-overview.html b/attack-theme/templates/general/attackcon-overview.html index b33437e1cac..380986eee32 100644 --- a/attack-theme/templates/general/attackcon-overview.html +++ b/attack-theme/templates/general/attackcon-overview.html @@ -5,9 +5,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -117,5 +115,5 @@

Sponsors

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/attack-theme/templates/general/base-template.html b/attack-theme/templates/general/base-template.html index 3fad1450130..2d58912ceb4 100644 --- a/attack-theme/templates/general/base-template.html +++ b/attack-theme/templates/general/base-template.html @@ -161,7 +161,6 @@ - {% endblock %} \ No newline at end of file diff --git a/attack-theme/templates/general/faq-overview.html b/attack-theme/templates/general/faq-overview.html index b1b039b1d1d..0abe4d06579 100644 --- a/attack-theme/templates/general/faq-overview.html +++ b/attack-theme/templates/general/faq-overview.html @@ -5,9 +5,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -64,5 +62,5 @@
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/attack-theme/templates/general/intro-overview.html b/attack-theme/templates/general/intro-overview.html index 29e410b8abc..d4eb1b182c4 100644 --- a/attack-theme/templates/general/intro-overview.html +++ b/attack-theme/templates/general/intro-overview.html @@ -4,26 +4,31 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} -{{ super () }} - -
-

{{ page.title }}

+
+ +
+
+
+
+
+

{{ page.title }}

{{ page.content }}
+
+
+
{% endblock %} {% block scripts %} {{ super () }} - + {% endblock %} diff --git a/attack-theme/templates/general/sidebar-resources-template.html b/attack-theme/templates/general/sidebar-resources-template.html new file mode 100644 index 00000000000..e2876bd9d4f --- /dev/null +++ b/attack-theme/templates/general/sidebar-resources-template.html @@ -0,0 +1,9 @@ +{% set RESOURCE_NAV = ${RESOURCE_NAV} -%} +{% import 'macros/navigation.html' as navigation %} +
+ {{navigation.sidenav(RESOURCE_NAV, output_file)}} +
+{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/attack-theme/templates/general/sidebar-template.html b/attack-theme/templates/general/sidebar-template.html new file mode 100644 index 00000000000..35887ffb5c8 --- /dev/null +++ b/attack-theme/templates/general/sidebar-template.html @@ -0,0 +1,9 @@ +{% import 'macros/navigation.html' as navigation %} +{% set parsed = page.data | from_json %} +
+ {{ navigation.sidenav(parsed.menu, output_file) }} +
+{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/attack-theme/templates/general/two-column.html b/attack-theme/templates/general/two-column.html index de595fdd145..74c51701f5d 100644 --- a/attack-theme/templates/general/two-column.html +++ b/attack-theme/templates/general/two-column.html @@ -4,7 +4,7 @@ {% block left %} {{ super() }} @@ -22,3 +22,8 @@ {% endblock %} {% endblock %} +{% block scripts %} +{{ super() }} + + +{% endblock %} diff --git a/attack-theme/templates/macros/datasource_table.html b/attack-theme/templates/macros/datasource_table.html index 6a18cd05f05..062a57a5e4c 100644 --- a/attack-theme/templates/macros/datasource_table.html +++ b/attack-theme/templates/macros/datasource_table.html @@ -1,6 +1,7 @@ {% import 'macros/clean_output.html' as clean_output %} {% macro datasource_table(datasources, show_descriptions, citations=None) %} +
@@ -50,4 +51,5 @@ {% endfor %}
+
{% endmacro %} \ No newline at end of file diff --git a/attack-theme/templates/macros/navigation.html b/attack-theme/templates/macros/navigation.html index 4a385b5f279..41e86328ec5 100644 --- a/attack-theme/templates/macros/navigation.html +++ b/attack-theme/templates/macros/navigation.html @@ -20,7 +20,7 @@ --> {% macro sidenav(root, output_file, filter=False) %}
-
{{root.name | upper}} +
{{root.name | upper}}
@@ -41,8 +41,7 @@
{% endif %}
- {% endmacro %} diff --git a/attack-theme/templates/macros/navigation_menu.html b/attack-theme/templates/macros/navigation_menu.html index 1aa97d850b2..b77fd46285b 100644 --- a/attack-theme/templates/macros/navigation_menu.html +++ b/attack-theme/templates/macros/navigation_menu.html @@ -48,7 +48,7 @@ +{% endmacro %} + +{% macro child_has_child(element) %} + + {% endmacro %} \ No newline at end of file diff --git a/attack-theme/templates/macros/techniques_used.html b/attack-theme/templates/macros/techniques_used.html index 21e5d5cf626..e707f7738b7 100644 --- a/attack-theme/templates/macros/techniques_used.html +++ b/attack-theme/templates/macros/techniques_used.html @@ -4,6 +4,7 @@ {% if title %}

{{title}}

{% endif %} +
@@ -133,4 +134,5 @@

{{title}}

{% endfor %}
+
{% endmacro %} \ No newline at end of file diff --git a/data/attackcon.json b/data/attackcon.json index da1942107cf..799f6e56af8 100644 --- a/data/attackcon.json +++ b/data/attackcon.json @@ -375,10 +375,10 @@ { "title": "What’s New with ATT&CK for Cloud?", "presenters": [{ - "names": ["Jen Burns"], + "names": ["Jack Burns"], "organization": "MITRE" }], - "description": "Jen Burns is a Lead Cybersecurity Engineer at MITRE and the Lead for MITRE ATT&CK for Cloud. She’s also a red team developer and lead for ATT&CK Evaluations, using her skills in software engineering and adversary emulation. Previously, she was a tech lead at HubSpot on the Infrastructure Security team where she focused on red teaming and building detections in the cloud environment. This presentation is from the MITRE ATT&Ckcon Power Hour session held on October 9, 2020.", + "description": "Jack Burns is a Lead Cybersecurity Engineer at MITRE and the Lead for MITRE ATT&CK for Cloud. He’s also a red team developer and lead for ATT&CK Evaluations, using his skills in software engineering and adversary emulation. Previously, he was a tech lead at HubSpot on the Infrastructure Security team where he focused on red teaming and building detections in the cloud environment. This presentation is from the MITRE ATT&Ckcon Power Hour session held on October 9, 2020.", "video": "https://www.youtube.com/watch?v=a-xs5VqlcKI&list=PLkTApXQou_8KKl3fOIPKD_9pNpmSe1TKu&index=24", "slides": "https://www.slideshare.net/attackcon2018/whats-new-with-attck-for-cloud" }, @@ -1139,4 +1139,4 @@ "slides": "https://www.slideshare.net/attackcon2018/mitre-attckcon-2018-the-use-of-game-theory-with-mitre-attck-matt-summers-and-nick-dunn-ncc-group" } ] -}] \ No newline at end of file +}] diff --git a/data/resources_navigation.json b/data/resources_navigation.json index d9c71f7884f..08a20f1c4b8 100644 --- a/data/resources_navigation.json +++ b/data/resources_navigation.json @@ -14,6 +14,12 @@ "path": "/resources/getting-started/", "children": [] }, + { + "name": "Contribute", + "id": "contribute", + "path": "/resources/contribute/", + "children": [] + }, { "name": "Training", "id": "training", @@ -69,6 +75,12 @@ "path": "/resources/brand/", "children": [] }, + { + "name": "Benefactors", + "id": "benefactors", + "path": "/resources/benefactors/", + "children": [] + }, { "name": "Privacy Policy", "id": "privacy", @@ -86,6 +98,12 @@ "id": "changelog", "path": "/resources/changelog.html", "children": [] + }, + { + "name": "Contact", + "id": "contact", + "path": "/contact/", + "children": [] } ] } diff --git a/modules/assets/__init__.py b/modules/assets/__init__.py new file mode 100644 index 00000000000..f39002b53e5 --- /dev/null +++ b/modules/assets/__init__.py @@ -0,0 +1,9 @@ +from . import assets +from . import assets_config + + +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 new file mode 100644 index 00000000000..25c166e201e --- /dev/null +++ b/modules/assets/assets.py @@ -0,0 +1,248 @@ +import collections +import json +import os + +from loguru import logger + +from modules import util + +from . import assets_config +from .. import site_config + + +def generate_assets(): + """Responsible for verifying asset directory and starting off asset markdown generation.""" + # Create content pages directory if does not already exist + util.buildhelpers.create_content_pages_dir() + + # Move templates to templates directory + util.buildhelpers.move_templates(assets_config.module_name, assets_config.assets_templates_path) + + # Verify if directory exists + if not os.path.isdir(assets_config.asset_markdown_path): + os.mkdir(assets_config.asset_markdown_path) + + # Generate redirections + util.buildhelpers.generate_redirections( + redirections_filename=assets_config.assets_redirection_location, redirect_md=site_config.redirect_md + ) + + # Generates the markdown files to be used for page generation + assets_generated = generate_markdown_files() + + if not assets_generated: + util.buildhelpers.remove_module_from_menu(assets_config.module_name) + + +def generate_markdown_files(): + """ + Responsible for generating asset index page and getting shared data for all assets. + """ + has_asset = False + + asset_list = util.relationshipgetters.get_asset_list() + + asset_list_no_deprecated_revoked = util.buildhelpers.filter_deprecated_revoked(asset_list) + + if asset_list_no_deprecated_revoked: + has_asset = True + + if has_asset: + data = {} + + notes = util.relationshipgetters.get_objects_using_notes() + 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: + md_file.write(subs) + + # Create the markdown for assets + for asset in asset_list: + generate_asset_md(asset, side_menu_data, notes) + + return has_asset + + +def generate_asset_md(asset, side_menu_data, notes): + """Responsible for generating markdown of all assets.""" + + attack_id = util.buildhelpers.get_attack_id(asset) + + if not attack_id: return + + data = {} + data["attack_id"] = attack_id + data["side_menu_data"] = side_menu_data + data["notes"] = notes.get(asset["id"]) + + dates = util.buildhelpers.get_created_and_modified_dates(asset) + if dates.get("created"): + data["created"] = dates["created"] + if dates.get("modified"): + data["modified"] = dates["modified"] + if asset.get("name"): + data["name"] = asset["name"] + if asset.get("x_mitre_version"): + data["version"] = asset["x_mitre_version"] + + if isinstance(asset.get("x_mitre_contributors"), collections.abc.Iterable): + data["contributors_list"] = asset["x_mitre_contributors"] + + if asset.get("x_mitre_platforms"): + asset["x_mitre_platforms"].sort() + data["platforms"] = ", ".join(asset["x_mitre_platforms"]) + + if asset.get("x_mitre_sectors"): + asset["x_mitre_sectors"].sort() + data["sectors"] = ", ".join(asset["x_mitre_sectors"]) + + # Get initial reference list + reference_list = {"current_number": 0} + + # Get initial reference list from asset object + reference_list = util.buildhelpers.update_reference_list(reference_list, asset) + + if asset.get("description"): + data["descr"] = asset["description"] + if asset.get("x_mitre_deprecated"): + data["deprecated"] = True + + # Get technique data for technique table + data["technique_table_data"] = get_techniques_targeting_asset_data(asset, reference_list) + + # Get navigator layers for this asset + layers = util.buildhelpers.get_navigator_layers( + data["name"], + data["attack_id"], + "asset", + "targeted by", + data["version"] if "version" in data else None, + data["technique_table_data"], + ) + + data["layers"] = [] + for layer in layers: + with open( + os.path.join( + assets_config.asset_markdown_path, + "-".join([data["attack_id"], "techniques", layer["domain"]]) + ".md", + ), + "w", + encoding="utf8", + ) as layer_json: + subs = site_config.layer_md.substitute( + { + "attack_id": data["attack_id"], + "path": "assets/" + data["attack_id"], + "domain": layer["domain"], + } + ) + subs = subs + layer["layer"] + layer_json.write(subs) + data["layers"].append( + { + "domain": layer["domain"], + "name": layer["name"], + "filename": layer["filename"], + "navigator_link": site_config.navigator_link, + } + ) + + if asset.get("x_mitre_related_assets"): + data["related_assets_table"] = get_related_asset_data(asset["x_mitre_related_assets"]) + + data["citations"] = reference_list + data["versioning_feature"] = site_config.check_versions_module() + + subs = assets_config.asset_md.substitute(data) + subs = subs + json.dumps(data) + + # Write out the markdown file + with open( + os.path.join(assets_config.asset_markdown_path, data["attack_id"] + ".md"), "w", encoding="utf8" + ) as md_file: + md_file.write(subs) + + +def get_assets_table_data(asset_list): + """Responsible for generating asset table data for the asset index page""" + assets_table_data = [] + for asset in asset_list: + attack_id = util.buildhelpers.get_attack_id(asset) + if not attack_id: continue + + domain_list = util.buildhelpers.get_domain_name(asset) + row = { + "id": attack_id, + "name": asset["name"] if asset.get("name") else attack_id, + } + + for domain_idx in range(len(domain_list)): + 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: + domain_list[domain_idx] = domain_list[domain_idx].capitalize() + row["domains"] = domain_list + + if asset.get("description"): + row["descr"] = asset["description"] + + if asset.get("x_mitre_deprecated"): + row["deprecated"] = True + + assets_table_data.append(row) + + return assets_table_data + + +def get_related_asset_data(related_assets): + if not related_assets: return [] + + related_asset_data = [] + for related_asset in related_assets: + row = { + "name": related_asset["name"], # required + } + if related_asset.get("related_asset_sectors"): + related_asset["related_asset_sectors"].sort() + row["sectors"] = ", ".join(related_asset["related_asset_sectors"]) + if related_asset.get("description"): + row["descr"] = related_asset["description"] + related_asset_data.append(row) + return related_asset_data + + +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. + """ + technique_list = {} + techniques_targeting_assets = util.relationshipgetters.get_techniques_targeting_assets() + + if techniques_targeting_assets.get(asset.get("id")): + for technique in techniques_targeting_assets[asset["id"]]: + # Do not add if technique is deprecated + if not technique["object"].get("x_mitre_deprecated"): + technique_list = util.buildhelpers.technique_used_helper(technique_list, technique, reference_list) + + technique_data = [] + for item in technique_list: + technique_data.append(technique_list[item]) + # Sort by technique name + technique_data = sorted(technique_data, key=lambda k: k["name"].lower()) + + # Sort by domain name + technique_data = sorted( + technique_data, key=lambda k: [site_config.custom_alphabet.index(c) for c in k["domain"].lower()] + ) + return technique_data diff --git a/modules/assets/assets_config.py b/modules/assets/assets_config.py new file mode 100644 index 00000000000..deafdb527d0 --- /dev/null +++ b/modules/assets/assets_config.py @@ -0,0 +1,22 @@ +from string import Template + +module_name = "Assets" +priority = 7.2 + +# Markdown path for assets +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: " +) + +# String template for asset page +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/" + +assets_redirection_location = "modules/assets/assets_redirections.json" diff --git a/modules/assets/assets_redirections.json b/modules/assets/assets_redirections.json new file mode 100644 index 00000000000..16b713b22f4 --- /dev/null +++ b/modules/assets/assets_redirections.json @@ -0,0 +1,7 @@ +[ + { + "title": "assets-redirect", + "from": "assets.html", + "to": "/assets/" + } +] diff --git a/modules/assets/templates/asset.html b/modules/assets/templates/asset.html new file mode 100644 index 00000000000..0dfb2452ac9 --- /dev/null +++ b/modules/assets/templates/asset.html @@ -0,0 +1,172 @@ +{% extends "general/two-column.html" %} +{% set parsed = page.data | from_json %} +{% set title = parsed.name + ", Asset " + parsed.attack_id + " | MITRE ATT&CK®" -%} +{% set active_page = "assets" -%} + +{% import 'macros/deprecated.html' as deprecated %} +{% import 'macros/navigator.html' as navigator %} +{% import 'macros/references.html' as references %} +{% import 'macros/navigation.html' as navigation %} +{% import 'macros/techniques_used.html' as techniques_used %} +{% import 'macros/versioning.html' as versioning %} +{% import 'macros/clean_output.html' as clean_output %} +{% import 'macros/citations.html' as citations %} +{% import 'macros/notes.html' as notes %} + +{% block head %} + {{ super () }} + {% if parsed.deprecated %} + + {% endif %} +{% endblock %} + +{% block innerleft %} + +
+ {{ navigation.sidenav(parsed.side_menu_data, output_file) }} +
+ +{% endblock %} + +{% block innerright %} +{{ super () }} + + +
+
+
+
+
+

{{ parsed.name }}

+ + {% if parsed.deprecated %} + {% if parsed.descr %} + {{ deprecated.deprecated(clean_output.stixToHTML(parsed.descr, firstParagraphOnly=True)) }} + {% else %} + {{ deprecated.deprecated("This asset has been deprecated.") }} + {% endif %} + {% else %} +
+
+ {% if parsed.descr %} +
+ {{ clean_output.stixToHTML(parsed.descr, parsed.citations) }} +
+ {% endif %} + {% if parsed.notes %} + {{ notes.notes_section(parsed.notes) }} + {% endif %} +
+
+
+
+ {% if parsed.attack_id %} +
+
+ ID: {{ parsed.attack_id }} +
+
+ {% endif %} + {% if parsed.platforms %} +
+
+ +
+
+ {{ "Platform" if parsed.platforms | length < 2 else "Platforms" }}: {{parsed.platforms}} +
+
+ {% endif %} + {% if parsed.sectors %} +
+
+ {{ "Sector" if parsed.sectors | length < 2 else "Sectors" }}: {{parsed.sectors}} +
+
+ {% endif %} + {% if parsed.contributors_list %} +
+
+ Contributors: {{ parsed.contributors_list|join("; ") }} +
+
+ {% endif %} + {% if parsed.version %} +
+
+ Version: {{ parsed.version }} +
+
+ {% endif %} + {% if parsed.created %} +
+
+ Created: {{ parsed.created }} +
+
+ {% endif %} + {% if parsed.modified %} +
+
+ Last Modified: {{ parsed.modified }} +
+
+ {% endif %} +
+
+ {% if parsed.versioning_feature %} + {{ versioning.permalink_button(output_file, parsed.attack_id)}} + {% endif %} +
+
+ + {% if parsed.related_assets_table %} +

Related Assets

+ + + + + + + + + + {% for related_asset in parsed.related_assets_table %} + + + + + + {% endfor %} + +
NameSectorsDescription
{{ related_asset.name }}{{ related_asset.sectors }} + {{ clean_output.stixToHTML(related_asset.descr, parsed.citations) }} +
+ {% endif %} + + {% if parsed.technique_table_data %} + {{ navigator.layer_links(parsed.attack_id, "assets", parsed.layers) }} + {{ techniques_used.techniques_used(parsed.technique_table_data, "Techniques", true, "", parsed.citations) }} + {% endif %} + + {{ citations.reference_section(parsed.citations) }} + {% endif %} +
+
+
+
+ +{% endblock %} + +{% block scripts %} + {{ super() }} + + + + + +{% endblock %} diff --git a/modules/assets/templates/assets-index.html b/modules/assets/templates/assets-index.html new file mode 100644 index 00000000000..7c3d9f910d3 --- /dev/null +++ b/modules/assets/templates/assets-index.html @@ -0,0 +1,80 @@ +{% extends "general/two-column.html" %} +{% set title = "Assets | MITRE ATT&CK®" %} +{% set parsed = page.data | from_json %} +{% set active_page = "assets" -%} +{% import 'macros/navigation.html' as navigation %} +{% import 'macros/clean_output.html' as clean_output %} + +{% block innerleft %} + +
+ {{ navigation.sidenav(parsed.side_menu_data, output_file) }} +
+ +{% endblock %} + +{% block innerright %} + {{ super () }} + +
+
+
+
+
+
+

Assets

+

+ <PLACEHOLDER DESCRIPTION> +

+ +
Assets: {{ parsed.assets_list_len }}
+ + + + + + + + + + + {% for row in parsed.assets_table %} + + + + + + + {% endfor %} + +
IDNameDomainDescription
+ {{ row.id | upper }} + + {{ row.name }} + + {% for dom in row.domains %} + {{dom}} +
+ {% endfor %} +
+ {% if row.deprecated %} + ****Deprecation Warning**** + {% endif %} + {{ clean_output.stixToHTML(row.descr) }} +
+
+
+
+
+
+ +{% endblock %} + +{% block scripts %} + {{ super() }} + + +{% endblock %} \ No newline at end of file diff --git a/modules/benefactors/__init__.py b/modules/benefactors/__init__.py new file mode 100644 index 00000000000..f21046ea539 --- /dev/null +++ b/modules/benefactors/__init__.py @@ -0,0 +1,21 @@ +from . import benefactors +from . import benefactors_config +import json + + +def get_priority(): + return benefactors_config.priority + + +def get_menu(): + return { + "display_name": "Benefactors", + "module_name": "Benefactors", + "url": "/resources/benefactors/", + "external_link": False, + "priority": benefactors_config.priority, + "children": [], + } + +def run_module(): + return (benefactors.generate_benefactors(), benefactors_config.module_name) diff --git a/modules/benefactors/benefactors.py b/modules/benefactors/benefactors.py new file mode 100644 index 00000000000..4861ab2fdbe --- /dev/null +++ b/modules/benefactors/benefactors.py @@ -0,0 +1,28 @@ +import json +import math +import os + +from modules import util + +from . import benefactors_config +from .. import site_config + + +def generate_benefactors(): + """Generate benefactors page markdown""" + + # Create content pages directory if does not already exist + util.buildhelpers.create_content_pages_dir() + + # Move templates to templates directory + util.buildhelpers.move_templates(benefactors_config.module_name, benefactors_config.benefactors_templates_path) + + # Create directory if it does not exist + if not os.path.isdir(benefactors_config.benefactors_markdown_path): + os.mkdir(benefactors_config.benefactors_markdown_path) + + 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: + md_file.write(benefactors_md) diff --git a/modules/benefactors/benefactors_config.py b/modules/benefactors/benefactors_config.py new file mode 100644 index 00000000000..ae8ae4db38f --- /dev/null +++ b/modules/benefactors/benefactors_config.py @@ -0,0 +1,12 @@ +module_name = "Benefactors" +priority = 9 + +# Markdown path for benefactors +benefactors_markdown_path = "content/pages/resources" + +# String template for benefactors index page +benefactors_md = ( + "Title: Benefactors\n" "Template: benefactors/benefactors\n" "save_as: resources/benefactors/index.html\n" "data: " +) + +benefactors_templates_path = "modules/benefactors/templates" diff --git a/modules/benefactors/templates/benefactors.html b/modules/benefactors/templates/benefactors.html new file mode 100644 index 00000000000..6b481dc6304 --- /dev/null +++ b/modules/benefactors/templates/benefactors.html @@ -0,0 +1,43 @@ +{% extends "general/two-column.html" %} +{% set active_page = "resources" -%} +{% set title = "Benefactors | MITRE ATT&CK®" -%} +{% import 'macros/navigation.html' as navigation %} + +{% block innerleft %} + +{% endblock %} + +{% block innerright %} +
+ +
+
+
+
+
+

Benefactors Program

+

MITRE ATT&CK® is a globally-accessible knowledge base of adversary tactics and techniques based on real-world observations. The ATT&CK knowledge base is used as a foundation for the development of specific threat models and methodologies in the private sector, in government, and in the cybersecurity product and service community. + Benefactor contributions help sustain ATT&CK's open-source operations and repositories, and will further research and development to FILL THIS IN.

+

Benefactors

+

Become a MITRE ATTACK Benefactor! Through your charitable giving you can directly help in sustaining and advancing the MITRE ATT&CK platform. Donations are directly applied to platform development and maintenance, academic and community engagement, and cutting-edge R&D. + To show our thanks, FILL THIS IN. + For more details see benefactor program link below.

+ +
+
+
+
+{% endblock %} +{% block scripts %} +{{ super () }} + + +{% endblock %} \ No newline at end of file diff --git a/modules/blog/blog_config.py b/modules/blog/blog_config.py index cd6759f8ece..b26974ea252 100644 --- a/modules/blog/blog_config.py +++ b/modules/blog/blog_config.py @@ -1,2 +1,2 @@ module_name = "Blog" -priority = 9 +priority = 10 diff --git a/modules/campaigns/__init__.py b/modules/campaigns/__init__.py index 7887aef5dae..99ff4eaa234 100644 --- a/modules/campaigns/__init__.py +++ b/modules/campaigns/__init__.py @@ -5,17 +5,5 @@ def get_priority(): return campaigns_config.priority - -def get_menu(): - return { - "display_name": campaigns_config.module_name, - "module_name": campaigns_config.module_name, - "url": "/campaigns", - "external_link": False, - "priority": campaigns_config.priority, - "children": [], - } - - def run_module(): return (campaigns.generate_campaigns(), campaigns_config.module_name) diff --git a/modules/campaigns/campaigns.py b/modules/campaigns/campaigns.py index 0a4eba57575..dba435d80ab 100644 --- a/modules/campaigns/campaigns.py +++ b/modules/campaigns/campaigns.py @@ -67,7 +67,7 @@ def generate_markdown_files(): data["campaigns_list_len"] = str(len(campaign_list_no_deprecated_revoked)) subs = campaigns_config.campaign_index_md + json.dumps(data) - + generate_sidebar_campaigns(side_menu_data) with open( os.path.join(campaigns_config.campaign_markdown_path, "overview.md"), "w", encoding="utf8" ) as md_file: @@ -141,6 +141,7 @@ def generate_campaign_md(campaign, side_menu_data, notes): data["name"], data["attack_id"], "campaign", + "used by", data["version"] if "version" in data else None, data["technique_table_data"], ) @@ -307,3 +308,16 @@ def get_software_table_data(campaign, reference_list): software_data = [software_list[item] for item in software_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") + data = {} + data["menu"] = side_menu_data + + # Sidebar Overview + 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: + md_file.write(sidebar_campaigns_md) diff --git a/modules/campaigns/campaigns_config.py b/modules/campaigns/campaigns_config.py index 94d0a5de8e5..3eae07cc697 100644 --- a/modules/campaigns/campaigns_config.py +++ b/modules/campaigns/campaigns_config.py @@ -20,3 +20,10 @@ campaigns_templates_path = "modules/campaigns/templates/" campaigns_redirection_location = "modules/campaigns/campaigns_redirections.json" + +sidebar_campaigns_md = ( + "Title: Campaigns Sidebar\n" + "Template: general/sidebar-template \n" + "save_as: campaigns/sidebar-campaigns/index.html\n" + "data: " +) \ No newline at end of file diff --git a/modules/campaigns/templates/campaign.html b/modules/campaigns/templates/campaign.html index 24ddfbc0fe5..874baadaa8f 100644 --- a/modules/campaigns/templates/campaign.html +++ b/modules/campaigns/templates/campaign.html @@ -27,9 +27,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -162,6 +160,7 @@

{{ parsed.name }}

{% if parsed.alias_descriptions %}

Associated Campaign Descriptions

+
@@ -182,10 +181,12 @@

Associated Campaign Descriptions

{% endfor %}
+
{% endif %} {% if parsed.group_data %}

Groups

+
@@ -212,6 +213,7 @@

Groups

{% endfor %}
+
{% endif %} {% if parsed.technique_table_data %} @@ -221,6 +223,7 @@

Groups

{% if parsed.software_data %}

Software

+
@@ -247,6 +250,7 @@

Software

{% endfor %}
+
{% endif %} {{ citations.reference_section(parsed.citations) }} @@ -261,8 +265,8 @@

Software

{% block scripts %} {{ super() }} - + {% endblock %} diff --git a/modules/campaigns/templates/campaigns-index.html b/modules/campaigns/templates/campaigns-index.html index 7787ed35046..9aa0e1c6ce2 100644 --- a/modules/campaigns/templates/campaigns-index.html +++ b/modules/campaigns/templates/campaigns-index.html @@ -7,9 +7,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -51,6 +49,7 @@

Campaigns

Campaigns: {{ parsed.campaigns_list_len }}
+
@@ -84,6 +83,7 @@
Campaigns: {{ parsed.campaigns_list_len }}
{% endfor %}
+
@@ -95,5 +95,5 @@
Campaigns: {{ parsed.campaigns_list_len }}
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/contribute/__init__.py b/modules/contribute/__init__.py deleted file mode 100644 index b81b657ddb8..00000000000 --- a/modules/contribute/__init__.py +++ /dev/null @@ -1,28 +0,0 @@ -from . import contribute -from . import contribute_config -import json - - -def get_priority(): - return contribute_config.priority - - -def get_menu(): - return { - "display_name": "Contribute", - "module_name": "Contribute", - "url": "/resources/contribute/", - "external_link": False, - "priority": contribute_config.priority, - "children": [], - } - - -def get_redirections(): - with open(contribute_config.contribute_redirection_location, "r", encoding="utf8") as json_redirections: - return json.load(json_redirections) - return [] - - -def run_module(): - return (contribute.generate_contribute(), contribute_config.module_name) diff --git a/modules/contribute/contribute.py b/modules/contribute/contribute.py deleted file mode 100644 index 6f9e2d52aa0..00000000000 --- a/modules/contribute/contribute.py +++ /dev/null @@ -1,61 +0,0 @@ -import json -import math -import os - -from modules import util - -from . import contribute_config -from .. import site_config - - -def generate_contribute(): - """Generate contribute page markdown""" - - # Create content pages directory if does not already exist - util.buildhelpers.create_content_pages_dir() - - # Move templates to templates directory - util.buildhelpers.move_templates(contribute_config.module_name, contribute_config.contribute_templates_path) - - # Generate redirections - util.buildhelpers.generate_redirections( - redirections_filename=contribute_config.contribute_redirection_location, redirect_md=site_config.redirect_md - ) - - ms = util.relationshipgetters.get_ms() - contributors = util.stixhelpers.get_contributors(ms) - - if not contributors: - util.buildhelpers.remove_module_from_menu(contribute_config.module_name) - return - - data = {} - - data["contributors"] = [] - - contributors_first_col = [] - contributors_second_col = [] - - half = math.ceil((len(contributors)) / 2) - list_size = len(contributors) - - for index in range(0, half): - contributors_first_col.append(contributors[index]) - - for index in range(half, list_size): - contributors_second_col.append(contributors[index]) - - data["contributors"].append(contributors_first_col) - data["contributors"].append(contributors_second_col) - - subs = contribute_config.contribute_index_md + json.dumps(data) - - # Create directory if it does not exist - if not os.path.isdir(contribute_config.contribute_markdown_path): - os.mkdir(contribute_config.contribute_markdown_path) - - # Open markdown file for the contribute page - with open( - os.path.join(contribute_config.contribute_markdown_path, "contribute.md"), "w", encoding="utf8" - ) as md_file: - md_file.write(subs) diff --git a/modules/contribute/contribute_config.py b/modules/contribute/contribute_config.py deleted file mode 100644 index 857319849bd..00000000000 --- a/modules/contribute/contribute_config.py +++ /dev/null @@ -1,14 +0,0 @@ -module_name = "Contribute" -priority = 10 - -# Markdown path for contribute -contribute_markdown_path = "content/pages/resources" - -# String template for contribution index page -contribute_index_md = ( - "Title: Contribute\n" "Template: contribute/contribute\n" "save_as: resources/contribute/index.html\n" "data: " -) - -contribute_templates_path = "modules/contribute/templates" - -contribute_redirection_location = "modules/contribute/contribute_redirections.json" diff --git a/modules/datasources/__init__.py b/modules/datasources/__init__.py index 722076992e1..56b61bcbdd9 100644 --- a/modules/datasources/__init__.py +++ b/modules/datasources/__init__.py @@ -9,12 +9,20 @@ def get_priority(): def get_menu(): return { - "display_name": datasources_config.module_name, + "display_name": datasources_config.module_tab_name, "module_name": datasources_config.module_name_no_spaces, "url": "/datasources", "external_link": False, "priority": datasources_config.priority, - "children": [], + "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": [] } + ] } diff --git a/modules/datasources/datasources_config.py b/modules/datasources/datasources_config.py index 220a45a81d3..584af6d1eff 100644 --- a/modules/datasources/datasources_config.py +++ b/modules/datasources/datasources_config.py @@ -2,6 +2,7 @@ module_name = "Data Sources" module_name_no_spaces = "datasources" +module_tab_name = "Defenses" priority = 4.1 @@ -22,4 +23,4 @@ ) # Path for templates -datasources_templates_path = "modules/datasources/templates/" +datasources_templates_path = "modules/datasources/templates/" \ No newline at end of file diff --git a/modules/datasources/templates/datasource.html b/modules/datasources/templates/datasource.html index 8e7ce56c470..9ec60ae9daf 100644 --- a/modules/datasources/templates/datasource.html +++ b/modules/datasources/templates/datasource.html @@ -177,7 +177,6 @@

{{parsed.name}}: {{datacomponent.name}}

{% endif %} - {% if datacomponent.techniques %} {% if datacomponent.add_datacomponent_ref %} {{techniques_used.techniques_used(datacomponent.techniques, "", true, "Detects", parsed.citations)}} @@ -185,7 +184,6 @@

{{parsed.name}}: {{datacomponent.name}}

{{techniques_used.techniques_used(datacomponent.techniques, "", true, "")}} {% endif %} {% endif %} - {% endfor %} @@ -205,6 +203,7 @@

{{parsed.name}}: {{datacomponent.name}}

+ diff --git a/modules/datasources/templates/datasources-index.html b/modules/datasources/templates/datasources-index.html index 9a9348972d4..6e1bbb91ec6 100644 --- a/modules/datasources/templates/datasources-index.html +++ b/modules/datasources/templates/datasources-index.html @@ -74,5 +74,6 @@
Data Sources: {{ parsed.datasources_list_len }} + {% endblock %} \ No newline at end of file diff --git a/modules/groups/__init__.py b/modules/groups/__init__.py index 96480c1aee3..abe5567a490 100644 --- a/modules/groups/__init__.py +++ b/modules/groups/__init__.py @@ -6,17 +6,19 @@ def get_priority(): return groups_config.priority - def get_menu(): return { - "display_name": groups_config.module_name, + "display_name": groups_config.module_tab_name, "module_name": groups_config.module_name, "url": "/groups", "external_link": False, "priority": groups_config.priority, - "children": [], + "children": [ + {"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.py b/modules/groups/groups.py index 5b2c888bd6c..e1ee4097f15 100644 --- a/modules/groups/groups.py +++ b/modules/groups/groups.py @@ -4,6 +4,8 @@ from modules import util +from loguru import logger + from . import groups_config from .. import site_config @@ -77,6 +79,7 @@ def generate_markdown_files(): for group in group_list: generate_group_md(group, side_menu_data, notes) + generate_sidebar_groups(side_menu_data) return has_group @@ -132,6 +135,7 @@ def generate_group_md(group, side_menu_data, notes): data["name"], data["attack_id"], "group", + "used by", data["version"] if "version" in data else None, data["technique_table_data"], inheritance, # extend legend to include color coding for inherited techniques, if applicable @@ -435,3 +439,16 @@ def update_software_list(pairings, software_list, reference_list, reference, id) software_list[software_stix_id]["techniques"].append(tech_data) return software_list, reference + +def generate_sidebar_groups(side_menu_data): + """Responsible for generating the sidebar for the groups pages.""" + logger.info("Generating groups sidebar") + data = {} + data["menu"] = side_menu_data + + # Sidebar Overview + sidebar_groups_md = groups_config.sidebar_groups_md + json.dumps(data) + + # write markdown to file + with open(os.path.join(groups_config.group_markdown_path, "sidebar_groups.md"), "w", encoding="utf8") as md_file: + md_file.write(sidebar_groups_md) diff --git a/modules/groups/groups_config.py b/modules/groups/groups_config.py index 24d8feb5d04..20a7da88a4e 100644 --- a/modules/groups/groups_config.py +++ b/modules/groups/groups_config.py @@ -1,6 +1,7 @@ from string import Template module_name = "Groups" +module_tab_name = "CTI" priority = 6 # Markdown path for groups @@ -16,3 +17,10 @@ groups_templates_path = "modules/groups/templates/" 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 diff --git a/modules/groups/templates/group.html b/modules/groups/templates/group.html index c2247770c39..bfab72e72d7 100644 --- a/modules/groups/templates/group.html +++ b/modules/groups/templates/group.html @@ -27,9 +27,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -141,6 +139,7 @@

{% if parsed.alias_descriptions %}

Associated Group Descriptions

+
@@ -161,10 +160,12 @@

Associated Group Descriptions

{% endfor %}
+
{% endif %} {% if parsed.campaign_data %}

Campaigns

+
@@ -205,6 +206,7 @@

Campaigns

{% endfor %}
+
{% endif %} {% if parsed.technique_table_data %} @@ -214,6 +216,7 @@

Campaigns

{% if parsed.software_data %}

Software

+
@@ -252,6 +255,7 @@

Software

{% endfor %}
+
{% endif %} {{ citations.reference_section(parsed.citations) }} @@ -266,7 +270,7 @@

Software

{% block scripts %} {{ super() }} - + diff --git a/modules/groups/templates/groups-index.html b/modules/groups/templates/groups-index.html index 382d907dd7a..aa3ea66b7bd 100644 --- a/modules/groups/templates/groups-index.html +++ b/modules/groups/templates/groups-index.html @@ -7,9 +7,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -45,6 +43,7 @@

Groups

Groups: {{ parsed.groups_list_len }}
+
@@ -76,6 +75,7 @@
Groups: {{ parsed.groups_list_len }}
{% endfor %}
+
@@ -87,5 +87,5 @@
Groups: {{ parsed.groups_list_len }}
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/matrices/matrices.py b/modules/matrices/matrices.py index bfd8a99ac07..22a3ab254bf 100644 --- a/modules/matrices/matrices.py +++ b/modules/matrices/matrices.py @@ -27,7 +27,7 @@ def generate_matrices(): notes = util.relationshipgetters.get_objects_using_notes() side_menu_data = util.buildhelpers.get_side_menu_matrices(matrices_config.matrices) - + generate_sidebar_matrices(side_menu_data) matrix_generated = False for matrix in matrices_config.matrices: @@ -259,3 +259,16 @@ 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") + data = {} + data["menu"] = side_menu_data + + # Sidebar Overview + 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: + md_file.write(sidebar_matrices_md) diff --git a/modules/matrices/matrices_config.py b/modules/matrices/matrices_config.py index ef8f4d31377..6ecf34d27b8 100644 --- a/modules/matrices/matrices_config.py +++ b/modules/matrices/matrices_config.py @@ -30,6 +30,13 @@ "data: " ) +sidebar_matrices_md = ( + "Title: Matrices Sidebar\n" + "Template: general/sidebar-template \n" + "save_as: matrices/sidebar-matrices/index.html\n" + "data: " +) + # The tree of matricies on /matrices/ matrices = [ { diff --git a/modules/matrices/templates/matrix.html b/modules/matrices/templates/matrix.html index a4c4624a9c5..fc1177aa7ea 100644 --- a/modules/matrices/templates/matrix.html +++ b/modules/matrices/templates/matrix.html @@ -30,7 +30,7 @@ {% block innerleft %} - {{ navigation.sidenav(parsed.menu, output_file) }} + {% endblock %} @@ -120,8 +120,8 @@

{{parsed.name}} {{matrix_plural}}

{{ super() }} - -{% endblock %} + + {% endblock %} diff --git a/modules/mitigations/__init__.py b/modules/mitigations/__init__.py index 704fd5eb3e5..40d74cfbd41 100644 --- a/modules/mitigations/__init__.py +++ b/modules/mitigations/__init__.py @@ -6,20 +6,5 @@ def get_priority(): return mitigations_config.priority -def get_menu(): - return { - "display_name": mitigations_config.module_name, - "module_name": mitigations_config.module_name, - "url": "/mitigations/", - "external_link": False, - "priority": mitigations_config.priority, - "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": []}, - ], - } - - def run_module(): return (mitigations.generate_mitigations(), mitigations_config.module_name) diff --git a/modules/mitigations/mitigations.py b/modules/mitigations/mitigations.py index e6e6eb64e3c..47a63bb020e 100644 --- a/modules/mitigations/mitigations.py +++ b/modules/mitigations/mitigations.py @@ -54,7 +54,7 @@ def generate_mitigations(): if not mitigation_generated: if check_if_generated: mitigation_generated = True - + generate_sidebar_mitigations(side_nav_data) if not mitigation_generated: util.buildhelpers.remove_module_from_menu(mitigations_config.module_name) @@ -137,6 +137,7 @@ def generate_mitigation_md(mitigation, domain, side_menu_data, notes): data["name"], data["attack_id"], "mitigation", + "mitigated by", data["version"] if "version" in data else None, data["techniques_addressed_data"], ) @@ -230,3 +231,16 @@ def get_techniques_addressed_data(mitigation, reference_list): technique_data, key=lambda k: [site_config.custom_alphabet.index(c) for c in k["domain"].lower()] ) return technique_data + +def generate_sidebar_mitigations(side_nav_data): + """Responsible for generating the sidebar for the mitigations pages.""" + logger.info("Generating mitigations sidebar") + data = {} + data["menu"] = side_nav_data + + # Sidebar Overview + 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: + md_file.write(sidebar_mitigations_md) diff --git a/modules/mitigations/mitigations_config.py b/modules/mitigations/mitigations_config.py index 54d0d2987a0..f60ea69f4b4 100644 --- a/modules/mitigations/mitigations_config.py +++ b/modules/mitigations/mitigations_config.py @@ -32,3 +32,10 @@ "save_as: mitigations/${attack_id}/index.html\n" "data: " ) + +sidebar_mitigations_md = ( + "Title: Mitigations Sidebar\n" + "Template: general/sidebar-template \n" + "save_as: mitigations/sidebar-mitigations/index.html\n" + "data: " +) \ No newline at end of file diff --git a/modules/mitigations/templates/mitigation.html b/modules/mitigations/templates/mitigation.html index 95515399a6a..261d8e1518b 100644 --- a/modules/mitigations/templates/mitigation.html +++ b/modules/mitigations/templates/mitigation.html @@ -26,9 +26,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -111,5 +109,5 @@

{% block scripts %} {{ super() }} - -{% endblock %} \ No newline at end of file + +{% endblock %} \ No newline at end of file diff --git a/modules/mitigations/templates/mitigations-domain-index.html b/modules/mitigations/templates/mitigations-domain-index.html index 509ead41ec5..9412fb49db5 100644 --- a/modules/mitigations/templates/mitigations-domain-index.html +++ b/modules/mitigations/templates/mitigations-domain-index.html @@ -14,9 +14,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -59,6 +57,7 @@

Mitigations: {{parsed.mitigation_list_len}}
+
@@ -83,6 +82,7 @@
Mitigations: {{parsed.mitigation_list_len}}
{% endfor %}
+
@@ -93,5 +93,5 @@
Mitigations: {{parsed.mitigation_list_len}}
{% block scripts %} {{ super() }} - -{% endblock %} \ No newline at end of file + +{% endblock %} \ No newline at end of file diff --git a/modules/random_page/random_page.py b/modules/random_page/random_page.py index 11236283fc0..604e7e65e17 100644 --- a/modules/random_page/random_page.py +++ b/modules/random_page/random_page.py @@ -16,6 +16,7 @@ def generate_json(): "groups": "Group", "software": "Software", "campaigns": "Campaign", + "assets": "Asset" } routes = {} @@ -57,6 +58,8 @@ def generate_json(): add_to_json = True elif route == "campaigns" and re.search(r"C[0-9]{4}", thepath): add_to_json = True + elif route == "assets" and re.search(r"A[0-9]{4}", thepath): + add_to_json = True if add_to_json: json_data[value].append(thepath[6:]) diff --git a/modules/resources/__init__.py b/modules/resources/__init__.py index 1f4e4eb7d35..fd1805b7fd2 100644 --- a/modules/resources/__init__.py +++ b/modules/resources/__init__.py @@ -22,6 +22,12 @@ def get_menu(): "external_link": False, "children": [], }, + { + "display_name": "Contribute", + "url": "/resources/contribute/", + "external_link": False, + "children": [], + }, {"display_name": "Training", "url": "/resources/training/", "external_link": False, "children": []}, {"display_name": "ATT&CKcon", "url": "/resources/attackcon/", "external_link": False, "children": []}, { diff --git a/modules/contribute/contribute_redirections.json b/modules/resources/contribute_redirections.json similarity index 100% rename from modules/contribute/contribute_redirections.json rename to modules/resources/contribute_redirections.json diff --git a/modules/resources/resources.py b/modules/resources/resources.py index 3b84026e294..4be25852801 100644 --- a/modules/resources/resources.py +++ b/modules/resources/resources.py @@ -3,6 +3,7 @@ import shutil from datetime import datetime from pathlib import Path +import math from loguru import logger from mitreattack.attackToExcel import attackToExcel @@ -37,16 +38,27 @@ def generate_resources(): if site_config.resource_nav["children"][i]["id"] == "versions": del site_config.resource_nav["children"][i] + build_benefactors_module = False + for module_info in modules.run_ptr: + if module_info["module_name"] == "benefactors": + build_benefactors_module = True + if not build_benefactors_module: + for i, child in enumerate(site_config.resource_nav["children"]): + if site_config.resource_nav["children"][i]["id"] == "benefactors": + del site_config.resource_nav["children"][i] + # Move templates to templates directory util.buildhelpers.move_templates(resources_config.module_name, resources_config.resources_templates_path) copy_docs(module_docs_path=resources_config.docs_path) generate_working_with_attack() generate_general_information() + generate_contribute_page() generate_training_pages() generate_brand_page() generate_attackcon_page() generate_faq_page() generate_static_pages() + generate_sidebar_resources() def copy_docs(module_docs_path): @@ -308,6 +320,7 @@ def generate_working_with_attack(): "techniques", "datasources", "campaigns", + "assets" ] # Verify if directories exists @@ -355,3 +368,53 @@ def generate_working_with_attack(): os.path.join(site_config.resources_markdown_path, "working_with_attack.md"), "w", encoding="utf8" ) as md_file: md_file.write(working_with_attack_content) + +def generate_sidebar_resources(): + """Responsible for generating the sidebar for the resource pages.""" + logger.info("Generating resource sidebar") + + # Sidebar Overview + 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: + md_file.write(sidebar_resources_md) + +def generate_contribute_page(): + """Responsible for generating the markdown pages of the contribute pages.""" + logger.info("Generating contributing page") + + # Generate redirections + util.buildhelpers.generate_redirections( + redirections_filename=resources_config.contribute_redirection_location, redirect_md=site_config.redirect_md + ) + + ms = util.relationshipgetters.get_ms() + contributors = util.stixhelpers.get_contributors(ms) + + data = {} + + data["contributors"] = [] + + contributors_first_col = [] + contributors_second_col = [] + + half = math.ceil((len(contributors)) / 2) + list_size = len(contributors) + + for index in range(0, half): + contributors_first_col.append(contributors[index]) + + for index in range(half, list_size): + contributors_second_col.append(contributors[index]) + + data["contributors"].append(contributors_first_col) + data["contributors"].append(contributors_second_col) + + 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: + md_file.write(subs) \ No newline at end of file diff --git a/modules/resources/resources_config.py b/modules/resources/resources_config.py index 5c438a74dad..de8c8f5eb81 100644 --- a/modules/resources/resources_config.py +++ b/modules/resources/resources_config.py @@ -34,6 +34,11 @@ "Template: resources/training-cti\n" "save_as: resources/training/cti/index.html\n" ) +contribute_md = ( + "Title: Contribute\n" "Template: resources/contribute\n" "save_as: resources/contribute/index.html\n" "data: " +) + +contribute_redirection_location = "modules/resources/contribute_redirections.json" # Brand md brand_md = "Title: Brand\n" "Template: resources/brand\n" "save_as: resources/brand/index.html\n" "data: " @@ -45,3 +50,9 @@ "save_as: resources/working-with-attack/index.html\n" "data: " ) + +sidebar_resources_md = ( + "Title: Resources Sidebar\n" + "Template: general/sidebar-resources \n" + "save_as: resources/sidebar-resources/index.html\n" +) diff --git a/modules/resources/static_pages/updates-april-2022.md b/modules/resources/static_pages/updates-april-2022.md index f0de48f0940..e201f87d147 100644 --- a/modules/resources/static_pages/updates-april-2022.md +++ b/modules/resources/static_pages/updates-april-2022.md @@ -806,10 +806,10 @@ ATT&CK for Mobile does not support data sources * Harshal Tupsamudre, Qualys * Hiroki Nagahama, NEC Corporation * Isif Ibrahima, Mandiant +* Jack Burns, HubSpot * James_inthe_box, Me * Jan Petrov, Citi * Jannie Li, Microsoft Threat Intelligence Center (MSTIC) -* Jen Burns, HubSpot * Jeremy Galloway * Joas Antonio dos Santos, @C0d3Cr4zy, Inmetrics * John Page (aka hyp3rlinx), ApparitionSec diff --git a/modules/resources/static_pages/updates-october-2021.md b/modules/resources/static_pages/updates-october-2021.md index 81a720457ec..68ddeb20052 100644 --- a/modules/resources/static_pages/updates-october-2021.md +++ b/modules/resources/static_pages/updates-october-2021.md @@ -781,10 +781,10 @@ No changes * Isif Ibrahima * Itamar Mizrahi, Cymptom * Ivan Sinyakov +* Jack Burns, HubSpot * Janantha Marasinghe * Jaron Bradley @jbradley89 * Jeff Felling, Red Canary -* Jen Burns, HubSpot * Joas Antonio dos Santos, @C0d3Cr4zy * Johann Rehberger * Jon Sheedy diff --git a/modules/resources/templates/attackcon.html b/modules/resources/templates/attackcon.html deleted file mode 100644 index cd7c8741522..00000000000 --- a/modules/resources/templates/attackcon.html +++ /dev/null @@ -1,137 +0,0 @@ -{% extends "general/two-column.html" %} -{% set active_page = "attackcon" -%} -{% set title = "MITRE ATT&CKcon | MITRE ATT&CK®" -%} -{% set conventions = page.data | from_json %} -{% import 'macros/navigation.html' as navigation %} - -{% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
-{% endblock %} - -{% block innerright %} - -
- -
- -
- -
- {% for con in conventions %} - {% set conID = "-".join(con.date.split(' ')) %} - {% set active = conID == 'March-2022' %} -
- - {% if "banner_img" in con %} -

- -

- {% endif %} - - {% if "description" in con %} -

{{con.description}}

- {% endif %} - {% if "blogpost" in con %} -

Click here to read our blog post about {{con.title}}! External site

- {% endif %} - -
-

Presentations

- {% for presentation in con.presentations %} - {% set isPanel = "moderator" in presentation %} -
- -
-
- {% if not isPanel %} -

{{presentation.description}}

- {% else %} -

Panelists:

-
    - {% for panelist in presentation.panelists %} -
  • - {{panelist}} -
  • - {% endfor %} -
-

{{presentation.moderator.name}} moderates a panel that discusses:

-
    - {% for topic in presentation.topics %} -
  • - {{topic}} -
  • - {% endfor %} -
- {% endif %} - - {% if "video" in presentation %} - video External site - {% else %} - (no video) - {% endif %} - - {% if "slides" in presentation %} - slides External site - {% else %} - (no slides) - {% endif %} -
-
-
- - {% endfor %} -
- {% if "sponsors_img" in con %} -
-

Sponsors

- -
- {% elif "sponsors_img_list" in con %} -
-

Sponsors

-
- {% for sponsor_img in con.sponsors_img_list %} -
- -
- {% endfor %} -
-
- {% endif %} -
- {% endfor %} -
-
- -{% endblock %} - - -{% block scripts %} -{{ super() }} - - -{% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/brand.html b/modules/resources/templates/brand.html index 4d6b68a78c0..24ab7d85e96 100644 --- a/modules/resources/templates/brand.html +++ b/modules/resources/templates/brand.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -78,5 +76,5 @@

Brand Guide

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/contact.html b/modules/resources/templates/contact.html index bac0a4d9b8d..b685b5fe773 100644 --- a/modules/resources/templates/contact.html +++ b/modules/resources/templates/contact.html @@ -1,10 +1,15 @@ -{% extends "general/base.html" %} +{% extends "general/two-column.html" %} {% set active_page = "contact" -%} {% set title = "Contact | MITRE ATT&CK®" %} +{% import 'macros/navigation.html' as navigation %} -{% block content %} +{% block innerleft %} + +{% endblock %} + +{% block innerright %} {{ super () }} -
+

Contact Us!

@@ -15,4 +20,10 @@

Contact Us!

For learning how to submit contributions to ATT&CK, read our contribute page

+{% endblock %} + +{% block scripts %} +{{ super() }} + + {% endblock %} \ No newline at end of file diff --git a/modules/contribute/templates/contribute.html b/modules/resources/templates/contribute.html similarity index 86% rename from modules/contribute/templates/contribute.html rename to modules/resources/templates/contribute.html index 9e092044aaf..56dc49bb4f4 100644 --- a/modules/contribute/templates/contribute.html +++ b/modules/resources/templates/contribute.html @@ -1,19 +1,27 @@ -{% extends "general/base.html" %} +{% extends "general/two-column.html" %} {% set active_page = "resources" -%} {% set title = "Contribute | MITRE ATT&CK®" -%} {% set parsed = page.data | from_json %} +{% import 'macros/navigation.html' as navigation %} -{% block content %} {{ super () }} -
-
- +
-
-

Contribute

+
+
+
+
+

Contribute

You can help contribute to ATT&CK.

@@ -92,18 +100,18 @@

Contribution Examples

New Technique Example
-
-
(Sub-)Technique Name:
-

COM, ROM, & BE GONE

-
Tactic:
-

Persistence

-
-
-
Platform:
-

Windows

-
Required Permissions:
-

User

-
+

+ (Sub-)Technique Name: COM, ROM, & BE GONE +

+

+ Tactic: Persistence +

+

+ Platform: Windows +

+

+ Required Permissions: User +

Sub-techniques: This is a sub-technique of T1XXX, or this would have T1XXX as a sub-technique

@@ -113,7 +121,7 @@
Required Permissions:

Description: Component Object Model (COM) servers - associated with Graphics Interchange Format (JIF) image viewers can be abused to corrupt arbitrary memory banks. Adversaries may leverage this opportunity to modify, mux, and maliciously annoy (MMA) read-only memory (ROM) regularly accessed during normal system operations. + associated with Graphics Interchange Format (JIF) image viewers can be abused to corrupt arbitrary memory banks. Adversaries may leverage this opportunity to modify, mux, and maliciously annoy (MMA) read-only memory (ROM) regularly accessed during normal system operations.

Detection: Monitor the JIF viewers for muxing @@ -121,14 +129,14 @@

Required Permissions:

Mitigation: Configure the Registry key - HKLM\SYSTEM\ControlSet\001\Control\WindowsJIFControl\ to 0 to disable MMA access - if not needed within the environment. + HKLM\SYSTEM\ControlSet\001\Control\WindowsJIFControl\ to 0 to disable MMA access + if not needed within the environment.

Adversary Use: Here is a publicly-available - reference about FUZZYSNUGGLYDUCK using this technique: - (www[.]awesomeThreatReports[.]org/FUZZYSNUGGLYDUCK_NOMS _ON_ROM_VIA_COM). - Additionally, our red team uses this in our operations. + reference about FUZZYSNUGGLYDUCK using this technique: + (www[.]awesomeThreatReports[.]org/FUZZYSNUGGLYDUCK_NOMS _ON_ROM_VIA_COM). + Additionally, our red team uses this in our operations.

Additional References: Here is a reference @@ -252,8 +260,12 @@

Contributors

Thanks to those who have contributed to ATT&CK!

+
-{% endblock %} {% block scripts %} {{ super () }} -{% endblock %} \ No newline at end of file +{% endblock %} +{% block scripts %} {{ super () }} + + +{% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/getting-started.html b/modules/resources/templates/getting-started.html index 846d46cb261..5f3151705aa 100644 --- a/modules/resources/templates/getting-started.html +++ b/modules/resources/templates/getting-started.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -263,5 +261,5 @@

Community

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/related-projects.html b/modules/resources/templates/related-projects.html index 8d8c4547e92..2de4b2615a8 100644 --- a/modules/resources/templates/related-projects.html +++ b/modules/resources/templates/related-projects.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -116,5 +114,5 @@
- + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/resources.html b/modules/resources/templates/resources.html index bab4d488e9b..b8dce9485e7 100644 --- a/modules/resources/templates/resources.html +++ b/modules/resources/templates/resources.html @@ -5,9 +5,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -157,5 +155,5 @@

Other Resources

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/training-cti.html b/modules/resources/templates/training-cti.html index a4678ca8422..17f2720005b 100644 --- a/modules/resources/templates/training-cti.html +++ b/modules/resources/templates/training-cti.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -284,5 +282,5 @@

Exercise 5: Making defensive recommendations {% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/training.html b/modules/resources/templates/training.html index aa624821f44..8c8be27956d 100644 --- a/modules/resources/templates/training.html +++ b/modules/resources/templates/training.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -49,5 +47,5 @@

ATT&CK Training

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/update-post.html b/modules/resources/templates/update-post.html index d860bc2ed33..bc48630d877 100644 --- a/modules/resources/templates/update-post.html +++ b/modules/resources/templates/update-post.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -24,5 +22,5 @@

{{ article.title }}

{% endblock %} {% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/updates-index.html b/modules/resources/templates/updates-index.html index 108694145f1..5bbcb8ca15c 100644 --- a/modules/resources/templates/updates-index.html +++ b/modules/resources/templates/updates-index.html @@ -5,9 +5,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} {{ super () }} @@ -26,5 +24,5 @@

{{ (articles|selectattr('template', 'equalto', {% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/resources/templates/working-with-attack.html b/modules/resources/templates/working-with-attack.html index b8973088de3..731e92094ba 100644 --- a/modules/resources/templates/working-with-attack.html +++ b/modules/resources/templates/working-with-attack.html @@ -6,9 +6,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -194,5 +192,5 @@

Explore our standa {% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/software/__init__.py b/modules/software/__init__.py index e9d0f6a651b..103553ec56d 100644 --- a/modules/software/__init__.py +++ b/modules/software/__init__.py @@ -5,16 +5,6 @@ def get_priority(): return software_config.priority -def get_menu(): - return { - "display_name": software_config.module_name, - "module_name": software_config.module_name, - "url": "/software/", - "external_link": False, - "priority": software_config.priority, - "children": [] - } - # 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: diff --git a/modules/software/software.py b/modules/software/software.py index b93a6faee0b..0feab0f498e 100644 --- a/modules/software/software.py +++ b/modules/software/software.py @@ -30,7 +30,6 @@ def generate_software(): # Generates the markdown files to be used for page generation and verifies if a software was generated software_generated = generate_markdown_files() - if not software_generated: util.buildhelpers.remove_module_from_menu(software_config.module_name) @@ -55,6 +54,7 @@ def generate_markdown_files(): side_menu_data = util.buildhelpers.get_side_menu_data( "software", "/software/", software_list_no_deprecated_revoked ) + generate_sidebar_software(side_menu_data) data["side_menu_data"] = side_menu_data data["software_table"] = get_software_table_data(software_list_no_deprecated_revoked) @@ -129,6 +129,7 @@ def generate_software_md(software, side_menu_data, notes): data["name"], data["attack_id"], "software", + "used by", data["version"] if "version" in data else None, data["technique_table_data"], ) @@ -350,3 +351,16 @@ def get_campaign_table_data(software, reference_list): campaign_data = [campaign_list[item] for item in campaign_list] campaign_data = sorted(campaign_data, key=lambda k: k["name"].lower()) return campaign_data + +def generate_sidebar_software(side_menu_data): + """Responsible for generating the sidebar for the software pages.""" + logger.info("Generating software sidebar") + data = {} + data["menu"] = side_menu_data + + # Sidebar Overview + sidebar_software_md = software_config.sidebar_software_md + json.dumps(data) + + # write markdown to file + with open(os.path.join(software_config.software_markdown_path, "sidebar_software.md"), "w", encoding="utf8") as md_file: + md_file.write(sidebar_software_md) \ No newline at end of file diff --git a/modules/software/software_config.py b/modules/software/software_config.py index 438da972ac9..304e3ca3a6f 100644 --- a/modules/software/software_config.py +++ b/modules/software/software_config.py @@ -21,4 +21,11 @@ "save_as: software/${attack_id}/index.html\n" "data: ") -software_redirection_location = "modules/software/software_redirections.json" \ No newline at end of file +software_redirection_location = "modules/software/software_redirections.json" + +sidebar_software_md = ( + "Title: Software Sidebar\n" + "Template: general/sidebar-template \n" + "save_as: software/sidebar-software/index.html\n" + "data: " +) \ No newline at end of file diff --git a/modules/software/templates/software-index.html b/modules/software/templates/software-index.html index 573481e8c33..0c43f5b1055 100644 --- a/modules/software/templates/software-index.html +++ b/modules/software/templates/software-index.html @@ -7,9 +7,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -46,6 +44,7 @@

Software

Software: {{ parsed.software_list_len }}
+
@@ -77,6 +76,7 @@
Software: {{ parsed.software_list_len }}
{% endfor %}
+

@@ -87,5 +87,5 @@
Software: {{ parsed.software_list_len }}
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/software/templates/software.html b/modules/software/templates/software.html index 0e1275cf6e0..3d606f47a9f 100644 --- a/modules/software/templates/software.html +++ b/modules/software/templates/software.html @@ -21,9 +21,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -144,6 +142,7 @@

{% if parsed.alias_descriptions %}

Associated Software Descriptions

+
@@ -164,6 +163,7 @@

Associated Software Descriptions

{% endfor %}
+
{% endif %} {% if parsed.technique_table_data %} @@ -173,6 +173,7 @@

Associated Software Descriptions

{% if parsed.groups %}

Groups That Use This Software

+
@@ -197,10 +198,12 @@

Groups That Use This Software

{% endfor %}
+
{% endif %} {% if parsed.campaign_data %}

Campaigns

+
@@ -227,6 +230,7 @@

Campaigns

{% endfor %}
+
{% endif %} {{ citations.reference_section(parsed.citations) }} @@ -242,7 +246,7 @@

Campaigns

{% block scripts %} {{ super() }} - + diff --git a/modules/tactics/tactics.py b/modules/tactics/tactics.py index 740f039fce0..2008ddbd57b 100644 --- a/modules/tactics/tactics.py +++ b/modules/tactics/tactics.py @@ -46,6 +46,7 @@ def generate_tactics(): tactics[domain["name"]] = util.stixhelpers.get_tactic_list(ms[domain["name"]], domain["name"]) side_nav_data = util.buildhelpers.get_side_nav_domains_data("tactics", tactics) + generate_sidebar_tactics(side_nav_data) for domain in site_config.domains: deprecated = True if domain["deprecated"] else False @@ -188,3 +189,17 @@ 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") + data = {} + data["menu"] = side_nav_data + + # Sidebar Overview + 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: + md_file.write(sidebar_tactics_md) + diff --git a/modules/tactics/tactics_config.py b/modules/tactics/tactics_config.py index d645a08bd38..b8dd5e4fa71 100644 --- a/modules/tactics/tactics_config.py +++ b/modules/tactics/tactics_config.py @@ -28,3 +28,10 @@ ) 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 diff --git a/modules/tactics/templates/tactic.html b/modules/tactics/templates/tactic.html index 5ae8e3aa715..fec15eaca49 100644 --- a/modules/tactics/templates/tactic.html +++ b/modules/tactics/templates/tactic.html @@ -25,9 +25,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -108,5 +106,5 @@

Techniques

{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/tactics/templates/tactics-domain-index.html b/modules/tactics/templates/tactics-domain-index.html index ab231368b91..ad0de01a2a4 100644 --- a/modules/tactics/templates/tactics-domain-index.html +++ b/modules/tactics/templates/tactics-domain-index.html @@ -23,9 +23,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.side_menu_data, output_file) }} -
+ {% endblock %} @@ -79,6 +77,7 @@
Tactics: {{ parsed.tactics_list_len }}
+
@@ -103,6 +102,7 @@
Tactics: {{ parsed.tactics_list_len }}
{% endfor %}
+
@@ -116,5 +116,5 @@
Tactics: {{ parsed.tactics_list_len }}
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/techniques/techniques.py b/modules/techniques/techniques.py index e5ab5e051c0..3d88ed39f3f 100644 --- a/modules/techniques/techniques.py +++ b/modules/techniques/techniques.py @@ -55,10 +55,10 @@ def generate_techniques(): check_if_generated = generate_domain_markdown( domain["name"], techniques_no_sub, tactics, side_nav_data, notes, deprecated ) - if not technique_generated: - if check_if_generated: - technique_generated = True + if not technique_generated and check_if_generated: + technique_generated = True + generate_sidebar_techniques(side_nav_data) if not technique_generated: util.buildhelpers.remove_module_from_menu(techniques_config.module_name) @@ -220,6 +220,9 @@ def generate_data_for_md(technique_dict, technique, tactic_list, is_sub_techniqu # Get examples technique_dict["examples_table"] = get_examples_table_data(technique, reference_list) + # Get asset table + technique_dict["assets_table"] = get_assets_table_data(technique, reference_list) + # Get technique version if technique.get("x_mitre_version"): technique_dict["version"] = technique["x_mitre_version"] @@ -357,26 +360,60 @@ def get_mitigations_table_data(technique, reference_list): # Do not add deprecated mitigation to table 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 attack_id: - 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"] - ) - row["descr"] = mitigation["relationship"]["description"] + 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"] + ) + row["descr"] = mitigation["relationship"]["description"] - mitigation_data.append(row) + mitigation_data.append(row) if mitigation_data: mitigation_data = sorted(mitigation_data, key=lambda k: k["name"].lower()) return mitigation_data +def get_assets_table_data(technique, reference_list): + """Given a technique a reference list, find assets that are targeted by the + technique and return list with asset data. Also modifies the + reference list if it finds a reference that is not on the list + """ + asset_data = [] + + # Check if technique has assets + assets_targeted_by_techniques = util.relationshipgetters.get_assets_targeted_by_techniques().get(technique["id"]) + if assets_targeted_by_techniques: + # Iterate through technique assets + for asset in assets_targeted_by_techniques: + # Do not add deprecated assets to table + if not asset["object"].get("x_mitre_deprecated"): + attack_id = util.buildhelpers.get_attack_id(asset["object"]) + + # Only add if attack id is found + 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"] + ) + row["descr"] = asset["relationship"]["description"] + + asset_data.append(row) + + if asset_data: + asset_data = sorted(asset_data, key=lambda k: k["name"].lower()) + return asset_data + + def get_examples_table_data(technique, reference_list): """Given a technique object, find examples in malware using technique, tools using technique and groups using technique. Return list with @@ -399,23 +436,23 @@ 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 attack_id: - row = {} + if not attack_id: continue + row = {} - row["id"] = attack_id + row["id"] = attack_id - row["path"] = get_path_from_type(example["object"]) + row["path"] = get_path_from_type(example["object"]) - row["name"] = example["object"]["name"] + row["name"] = example["object"]["name"] - if example["relationship"].get("description"): - # Get filtered description - reference_list = util.buildhelpers.update_reference_list( - reference_list, example["relationship"] - ) - row["descr"] = example["relationship"]["description"] + if example["relationship"].get("description"): + # Get filtered description + reference_list = util.buildhelpers.update_reference_list( + reference_list, example["relationship"] + ) + row["descr"] = example["relationship"]["description"] - example_data.append(row) + example_data.append(row) if example_data: example_data = sorted(example_data, key=lambda k: k["name"].lower()) @@ -507,20 +544,21 @@ 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 attack_id: - technique_dict = {} - technique_dict["id"] = attack_id - technique_dict["stix_id"] = technique["id"] - technique_dict["name"] = technique["name"] - technique_dict["description"] = technique["description"] + if not attack_id: continue + + technique_dict = {} + technique_dict["id"] = attack_id + technique_dict["stix_id"] = technique["id"] + technique_dict["name"] = technique["name"] + technique_dict["description"] = technique["description"] - if technique.get("kill_chain_phases"): - for elem in technique["kill_chain_phases"]: - # Fill dict - if elem["phase_name"] not in technique_list: - technique_list[elem["phase_name"]] = [] + if technique.get("kill_chain_phases"): + for elem in technique["kill_chain_phases"]: + # Fill dict + if elem["phase_name"] not in technique_list: + technique_list[elem["phase_name"]] = [] - technique_list[elem["phase_name"]].append(technique_dict) + technique_list[elem["phase_name"]].append(technique_dict) for key, __ in technique_list.items(): technique_list[key] = sorted(technique_list[key], key=lambda k: k["name"].lower()) @@ -531,8 +569,6 @@ def get_techniques_list(techniques): def get_subtechniques(technique): """Given a technique, return the ID and name of the subtechnique.""" subtechs = [] - attack_id = util.buildhelpers.get_attack_id(technique) - subtechniques_of = util.relationshipgetters.get_subtechniques_of() if technique["id"] in subtechniques_of: @@ -612,3 +648,16 @@ def get_datasources_and_components_of_technique(technique, reference_list): datasource_and_components = sorted(datasource_and_components, key=lambda k: k["name"].lower()) 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") + data = {} + data["menu"] = side_nav_data + + # Sidebar Overview + 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: + md_file.write(sidebar_techniques_md) diff --git a/modules/techniques/techniques_config.py b/modules/techniques/techniques_config.py index d3947171d3a..1679960fb7d 100644 --- a/modules/techniques/techniques_config.py +++ b/modules/techniques/techniques_config.py @@ -42,3 +42,10 @@ "save_as: techniques/${parent_id}/${sub_number}/index.html\n" "data: " ) + +sidebar_techniques_md = ( + "Title: Techniques Sidebar\n" + "Template: general/sidebar-template \n" + "save_as: techniques/sidebar-techniques/index.html\n" + "data: " +) \ No newline at end of file diff --git a/modules/techniques/templates/technique.html b/modules/techniques/templates/technique.html index 12faafebbbf..43259e9be5e 100644 --- a/modules/techniques/templates/technique.html +++ b/modules/techniques/templates/technique.html @@ -37,9 +37,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.menu, output_file) }} -
+ {% endblock %} @@ -342,6 +340,7 @@
Sub-techniques ({{parsed.subtechniques|len {% if parsed.examples_table %}

Procedure Examples

+
@@ -368,10 +367,35 @@

Procedure Examples

{% endfor %}
+
+ {% endif %} + {% if parsed.assets_table %} +

Targeted Assets

+ + + + + + + + + {% for asset in parsed.assets_table %} + + + + + {% endfor %} + +
IDAsset
+ {{asset.id}} + + {{asset.name}} +
{% endif %} {% if parsed.domain != 'pre' %}

Mitigations

{% if parsed.mitigation_table %} +
@@ -398,6 +422,7 @@

Mitigations

{% endfor %}
+
{% else %}

This type of attack technique cannot be easily mitigated with preventive controls since @@ -448,7 +473,6 @@

Difficulty for the Adversary

{% block scripts %} {{ super() }} - {% if parsed.is_subtechnique %} @@ -456,4 +480,5 @@

Difficulty for the Adversary

{% else %} {% endif %} + {% endblock %} \ No newline at end of file diff --git a/modules/techniques/templates/techniques-domain-index.html b/modules/techniques/templates/techniques-domain-index.html index 0f4d018a689..9cd8308b4ee 100644 --- a/modules/techniques/templates/techniques-domain-index.html +++ b/modules/techniques/templates/techniques-domain-index.html @@ -23,9 +23,7 @@ {% block innerleft %} -
- {{ navigation.sidenav(parsed.menu, output_file) }} -
+ {% endblock %} @@ -86,5 +84,5 @@
Sub-techniques: {{ parsed.subtechniques_len }}
{% block scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/modules/tests/linkchecker.py b/modules/tests/linkchecker.py index d385a2a0d1c..ad847506b8f 100644 --- a/modules/tests/linkchecker.py +++ b/modules/tests/linkchecker.py @@ -6,7 +6,7 @@ from loguru import logger from modules import site_config - +import modules from . import tests_config # STATIC PROPERTIES @@ -433,7 +433,8 @@ def check_links(external_links=False): if unlinked_pages: f.write("Pages listed were not linked from another page\n\n") for page in unlinked_pages: - f.write(page + "\n") + if 'sidebar' not in page: #remove the sidebar html pages from unlinked pages + f.write(page + "\n") else: f.write("No unlinked pages found\n") @@ -441,12 +442,21 @@ def check_links(external_links=False): broken_count = 0 with open(os.path.join(site_config.test_report_directory, tests_config.links_report_filename), "w") as f: f.write("Broken links report:\n\n") + build_versions_module = False + for module_info in modules.run_ptr: + if module_info["module_name"] == "versions": + build_versions_module = True if broken_pages: for page in broken_pages: f.write(page["path"] + "\n") for problem in page["problems"]: - f.write("\t- " + problem + "\n") - broken_count += 1 + if "versions" in problem: + if build_versions_module: + f.write("\t- " + problem + "\n") + broken_count += 1 + else: + f.write("\t- " + problem + "\n") + broken_count += 1 else: f.write("No broken links found\n") diff --git a/modules/util/buildhelpers.py b/modules/util/buildhelpers.py index ea0c6eb92ab..20b82e56d26 100644 --- a/modules/util/buildhelpers.py +++ b/modules/util/buildhelpers.py @@ -507,17 +507,17 @@ def replace_html_chars(to_be_replaced): } -def get_navigator_layers(name, attack_id, obj_type, version, techniques_used, inheritance=False): +def get_navigator_layers(name, attack_id, obj_type, rel_type, version, techniques_used, inheritance=False): """Generate the Enterprise, Mobile, and ICS Navigator JSON layers for the given object.""" # Generate Enterprise base layer - enterprise_layer = build_base_layer("enterprise-attack", name, obj_type, attack_id, version, inheritance) + enterprise_layer = build_base_layer("enterprise-attack", name, obj_type, rel_type, attack_id, version, inheritance) # Generate Mobile base layer - mobile_layer = build_base_layer("mobile-attack", name, obj_type, attack_id, version, inheritance) + mobile_layer = build_base_layer("mobile-attack", name, obj_type, rel_type, attack_id, version, inheritance) # Generate ICS base layer - ics_layer = build_base_layer("ics-attack", name, obj_type, attack_id, version, inheritance) + ics_layer = build_base_layer("ics-attack", name, obj_type, rel_type, attack_id, version, inheritance) # Add technique data to layer for technique in techniques_used: @@ -584,12 +584,12 @@ def get_navigator_layers(name, attack_id, obj_type, version, techniques_used, in return layers -def build_base_layer(domain, object_name, object_type, attack_id, version, inheritance=False): +def build_base_layer(domain, object_name, object_type, rel_type, attack_id, version, inheritance=False): """Build the base Navigator layer for the given object.""" layer = {} # Layer description - layer["description"] = f"{domain_name_map[domain]} techniques used by {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})" @@ -616,14 +616,14 @@ def build_base_layer(domain, object_name, object_type, attack_id, version, inher # Layer legend layer["legendItems"] = [ - {"label": f"used by {object_name}", "color": colorMap[1]} + {"label": f"{rel_type} {object_name}", "color": colorMap[1]} ] # Add campaign inheritance to legend, if applicable if inheritance: layer["legendItems"].extend([ - {"label": f"used by a campaign attributed to {object_name}", "color": colorMap[2]}, - {"label": f"used by {object_name} and used by a campaign attributed to {object_name}", "color": colorMap[3]} + {"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 diff --git a/modules/util/relationshipgetters.py b/modules/util/relationshipgetters.py index 8cd981f9a56..ff92451a1a0 100644 --- a/modules/util/relationshipgetters.py +++ b/modules/util/relationshipgetters.py @@ -11,6 +11,7 @@ techniques_used_by_tools = {} techniques_used_by_groups = {} techniques_used_by_campaigns = {} +techniques_targeting_assets = {} techniques_detected_by_datacomponent = {} groups_using_tool = {} groups_using_malware = {} @@ -20,6 +21,7 @@ tools_using_technique = {} malware_using_technique = {} groups_using_technique = {} +assets_targeted_by_techniques = {} campaigns_using_technique = {} campaigns_using_tool = {} campaigns_using_malware = {} @@ -44,6 +46,7 @@ datacomponent_list = [] mitigation_list = [] campaign_list = [] +asset_list = [] technique_to_domain = {} @@ -130,6 +133,25 @@ def get_techniques_used_by_campaigns(): return techniques_used_by_campaigns +def get_techniques_targeting_assets(): + """techniques targeting assets getter""" + global techniques_targeting_assets + + if not techniques_targeting_assets: + techniques_targeting_assets = rsh.techniques_targeting_assets(get_srcs()) + + return techniques_targeting_assets + + +def get_assets_targeted_by_techniques(): + """assets targeted by techniques getter""" + global assets_targeted_by_techniques + + if not assets_targeted_by_techniques: + assets_targeted_by_techniques = rsh.assets_targeted_by_techniques(get_srcs()) + + return assets_targeted_by_techniques + def get_techniques_detected_by_datacomponent(): global techniques_detected_by_datacomponent @@ -431,6 +453,15 @@ def get_campaign_list(): return campaign_list +def get_asset_list(): + """asset list getter""" + global asset_list + + if not asset_list: + asset_list = get_resources()["assets"] + + return asset_list + def get_technique_to_domain(): """technique to domain getter""" diff --git a/modules/util/relationshiphelpers.py b/modules/util/relationshiphelpers.py index 13f6ab734e1..3a8d42db620 100644 --- a/modules/util/relationshiphelpers.py +++ b/modules/util/relationshiphelpers.py @@ -247,6 +247,21 @@ def campaigns_attributed_to_group(srcs): return get_related(srcs, "campaign", "attributed-to", "intrusion-set", reverse=True) +# technique:asset +def techniques_targeting_assets(srcs): + """Return asset_id => {technique, relationship} for each technique targeting the asset. + + srcs should be an array of memorystores for enterprise, mobile, and pre + """ + 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. + + srcs should be an array of memorystores for enterprise, mobile, and pre + """ + 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/util/stixhelpers.py b/modules/util/stixhelpers.py index 3925b04fdfa..165b4ac6388 100644 --- a/modules/util/stixhelpers.py +++ b/modules/util/stixhelpers.py @@ -359,6 +359,9 @@ def get_domain_resources(types): # Generates list of campaigns campaign_list = get_domain_resources(["campaign"]) + # Generates list of assets + asset_list = get_domain_resources(["x-mitre-asset"]) + # Generates list of relationships rel_list = [] for domain in site_config.domains: @@ -377,6 +380,7 @@ def get_domain_resources(types): "techniques": tech_list, "mitigations": coa_list, "campaigns": campaign_list, + "assets": asset_list } return resources @@ -430,6 +434,7 @@ def get_contributors(ms): "x-mitre-data-component", "x-mitre-data-source", "x-mitre-tactic", + "x-mitre-asset", ] src = ms[domain["name"]] obj_list = [] diff --git a/modules/versions/templates/versions.html b/modules/versions/templates/versions.html index e317438935b..39b8aeff481 100644 --- a/modules/versions/templates/versions.html +++ b/modules/versions/templates/versions.html @@ -5,9 +5,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -32,6 +30,7 @@

Versions of ATT&CK

Below are a list of versions of the ATT&CK website preserved for posterity, including a permalink to the current version of the site:

+
@@ -78,6 +77,7 @@

Versions of ATT&CK

{% endfor %}
+
@@ -90,5 +90,5 @@

Versions of ATT&CK

- + {% endblock %} diff --git a/modules/website_build/templates/changelog.html b/modules/website_build/templates/changelog.html index c06d974bf2b..6366ad7bc91 100644 --- a/modules/website_build/templates/changelog.html +++ b/modules/website_build/templates/changelog.html @@ -4,9 +4,7 @@ {% import 'macros/navigation.html' as navigation %} {% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
+ {% endblock %} {% block innerright %} @@ -35,5 +33,5 @@

Changelog

{% endblock %} {% block scripts %} {{ super () }} - + {% endblock %} diff --git a/modules/website_build/templates/faq.html b/modules/website_build/templates/faq.html deleted file mode 100644 index 76f1799579f..00000000000 --- a/modules/website_build/templates/faq.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "general/two-column.html" %} -{% set active_page = "resources" -%} -{% set title = "FAQ | MITRE ATT&CK®" -%} -{% set parsed = page.data | from_json %} -{% import 'macros/navigation.html' as navigation %} - -{% block innerleft %} -
- {{navigation.sidenav(RESOURCE_NAV, output_file)}} -
-{% endblock %} - -{% block innerright %} - -
- -
-
-

Frequently Asked Questions

-
- {% for section in parsed.sections %} -
-

{{section.name}}

-
- {% for qa in section.questions %} -
- -
-
- {{qa.answer}} -
-
-
- {% endfor %} -
-
- {% endfor %} -
-
-{% endblock %} - -{% block scripts %} -{{ super() }} - - -{% endblock %} \ No newline at end of file diff --git a/modules/website_build/website_build.py b/modules/website_build/website_build.py index 1270d672d3c..b36e54c8fd7 100644 --- a/modules/website_build/website_build.py +++ b/modules/website_build/website_build.py @@ -31,6 +31,7 @@ def generate_website(): ) generate_javascript_settings() generate_base_html() + generate_sidebar_html() generate_index_page() generate_static_pages() generate_changelog_page() @@ -143,6 +144,17 @@ def generate_base_html(): with open(os.path.join(website_build_config.template_dir, "base.html"), "w", encoding="utf8") as base_template_f: base_template_f.write(subs) +def generate_sidebar_html(): + with open( + os.path.join(website_build_config.template_dir, "sidebar-resources-template.html"), "r", encoding="utf8" + ) as sidebar_template_f: + sidebar_template = sidebar_template_f.read() + sidebar_template = Template(sidebar_template) + subs = sidebar_template.substitute(website_build_config.sidebar_page_data) + + with open(os.path.join(website_build_config.template_dir, "sidebar-resources.html"), "w", encoding="utf8") as sidebar_template_f: + sidebar_template_f.write(subs) + def generate_index_page(): """Responsible for creating the landing page""" @@ -168,6 +180,7 @@ def generate_index_page(): "groups": "Group", "software": "Software", "campaigns": "Campaign", + "assets": "Asset", } routes = {} diff --git a/modules/website_build/website_build_config.py b/modules/website_build/website_build_config.py index 2220f1f8c27..86512e1b851 100644 --- a/modules/website_build/website_build_config.py +++ b/modules/website_build/website_build_config.py @@ -32,6 +32,10 @@ "RESOURCE_NAV": site_config.resource_nav, } +sidebar_page_data = { + "RESOURCE_NAV": site_config.resource_nav, +} + # config for the matrix shown on the index page index_matrix = { "name": "ATT&CK Matrix for Enterprise", diff --git a/requirements.txt b/requirements.txt index d7c280ad62f..c81ebfb916b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ stix2==3.0.1 stix2-validator==3.1.3 toml==0.10.2 towncrier==22.12.0 -urllib3==1.26.15 +urllib3==1.26.17 webassets==2.0 # dev dependencies diff --git a/update-attack.py b/update-attack.py index 602fd29cc56..4b4fc048bdb 100644 --- a/update-attack.py +++ b/update-attack.py @@ -22,6 +22,7 @@ "tactics", "techniques", "campaigns", + "assets", "tour", "website_build", "random_page", @@ -29,7 +30,7 @@ "subdirectory", "tests", ] -extras = ["resources", "versions", "contribute", "blog", "stixtests"] +extras = ["resources", "versions", "blog", "stixtests", "benefactors"] test_choices = ["size", "links", "external_links", "citations"] @@ -229,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)