Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added plugin to generate Java class hierachy that matches MITRE attac… #177

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ More detailed information and examples about the specific usage of the additiona
|:------------|:------------|:--------------|
| [navlayers](https://github.com/mitre-attack/mitreattack-python/tree/master/mitreattack/navlayers) | A collection of utilities for working with [ATT&CK Navigator](https://github.com/mitre-attack/attack-navigator) layers. Provides the ability to import, export, and manipulate layers. Layers can be read in from the filesystem or python dictionaries, combined and edited, and then exported to excel or SVG images. | Further documentation can be found [here](https://github.com/mitre-attack/mitreattack-python/blob/master/mitreattack/navlayers/README.md).|
| [attackToExcel](https://github.com/mitre-attack/mitreattack-python/tree/master/mitreattack/attackToExcel) | A collection of utilities for converting [ATT&CK STIX data](https://github.com/mitre/cti) to Excel spreadsheets. It also provides access to [Pandas](https://pandas.pydata.org/) DataFrames representing the dataset for use in data analysis. | Further documentation can be found [here](https://github.com/mitre-attack/mitreattack-python/blob/master/mitreattack/attackToExcel/README.md).|
| [attackToJava](https://github.com/mitre-attack/mitreattack-python/tree/master/mitreattack/attackToJava) | An utility for converting [ATT&CK STIX data](https://github.com/mitre/cti) to Java class hierarchy. It uses the Pandas dataframe from attackToExcel| Further documentation can be found [here](https://github.com/mitre-attack/mitreattack-python/blob/master/mitreattack/attackToJava/README.md).|
| [collections](https://github.com/mitre-attack/mitreattack-python/tree/master/mitreattack/collections) | A set of utilities for working with [ATT&CK Collections and Collection Indexes](https://github.com/center-for-threat-informed-defense/attack-workbench-frontend/blob/master/docs/collections.md). Provides functionalities for converting and summarizing data in collections and collection indexes, as well as generating a collection from a raw stix bundle input. | Further documentation can be found [here](https://github.com/mitre-attack/mitreattack-python/blob/master/mitreattack/collections/README.md).|
| [diffStix](https://github.com/mitre-attack/mitreattack-python/tree/master/mitreattack/diffStix) | Create markdown, HTML, JSON and/or ATT&CK Navigator layers reporting on the changes between two versions of the STIX2 bundles representing the ATT&CK content. Run `diff_stix -h` for full usage instructions. | Further documentation can be found [here](https://github.com/mitre-attack/mitreattack-python/blob/master/mitreattack/diffStix/README.md).|

Expand Down
93 changes: 93 additions & 0 deletions docs/attacktojava.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
ATT&CK to Java
==============================================

ATT&CK to Java contains a module for converting `ATT&CK STIX data <https://github.com/mitre/cti>`_ to Java class hierarchy.


Usage:
-----

Command Line
-----

Print full usage instructions:

.. code:: bash

python3 attackToJava.py -h


Example execution:

.. code:: bash

python3 attackToJava.py -output /tmp/attack


Build a Java files corresponding to a version of ATT&CK:

.. code:: bash

python3 attackToJava -output /tmp/attack -version v5.0



Interfaces:
-----

attackToJava
-----
attackToJava provides the means by which to convert/extract the ATT&CK STIX data to Java class hierarchy.
A brief overview of the available methods follows.

.. list-table:: Title
:widths: 33 33 34
:header-rows: 1

* - method name
- arguments
- usage
* - export
- `domain`: the domain of ATT&CK to download <br> `version`: optional parameter specifying which version of ATT&CK to download <br> `output_dir`: optional parameter specifying output directory
- `version` : The version of ATT&CK to download, e.g "v8.1". If omitted will build the current version of ATT&CK, by default None
- `output_dir` : The directory to write the Java files to.
- `remote` : The URL of a remote ATT&CK Workbench instance to connect to for stix data. Mutually exclusive with stix_file.
- `stix_file` : Path to a local STIX file containing ATT&CK data for a domain, by default None
- Download ATT&CK data from MITRE/CTI and convert it to Java class hierarchy

stixToJava
-----

stixToJava provides various methods to process and manipulate the STIX data in order to create Java

.. list-table:: Method Documentation
:widths: 33 33 34
:header-rows: 1

* - method name
- arguments
- usage
* - runMaven
- `output_dir`: str
- Run Maven to build the Java classes.<br>`output_dir`: The directory to run Maven in, by default "."
* - remove_tautology
- `text`: str
- Remove tautology from the text.<br>`text`: The text to process.<br>Returns the processed text without tautology.
* - formatTextToLines
- `text`: str<br>`max_line_length`: int = 80
- Format text to lines of a specified maximum length.<br>`text`: The text to format.<br>`max_line_length`: The maximum line length, by default 80.<br>Returns the formatted lines.
* - buildOutputDir
- `package_name`: str = None<br>`output_dir`: str = None
- Build the output directory for the Java classes.<br>`package_name`: The name of the package to create the directory for.<br>`output_dir`: The root directory for output.<br>Returns the path to the output directory.
* - nameToClassName
- `name`: str
- Convert a name to a class name.<br>`name`: The name to convert.<br>Returns the class name.
* - writeJinja2Template
- `templateEnv`: jinja2.Environment<br>`template_name`: str<br>`output_file`: str<br>`fields`: dict
- Write a Jinja2 template to a file.<br>`templateEnv`: The Jinja2 environment.<br>`template_name`: The template file to use.<br>`output_file`: The output file to write to.<br>`fields`: The fields to use in the template.
* - stixToTactics
- `stix_data`: MemoryStore<br>`package_name`: str<br>`domain`: str<br>`verbose_class`: bool = False<br>`output_dir`: str = "."
- Parse STIX tactics from the given data and write corresponding Java classes.<br>`stix_data`: MemoryStore or other stix2 DataSource object holding the domain data.<br>`package_name`: The base package name for the output Java classes.<br>`domain`: The domain of ATT&CK stix_data corresponds to, e.g., "enterprise-attack".<br>`verbose_class`: Whether to include verbose class information, by default False.<br>`output_dir`: The root directory for output, by default ".".
* - stixToTechniques
- `all_data_sources`: dict<br>`all_defenses_bypassed`: dict<br>`all_platforms`: dict<br>`stix_data`: MemoryStore<br>`package_name`: str<br>`domain`: str<br>`verbose_class`: bool = False<br>`output_dir`: str = "."
- Parse STIX techniques from the given data and write corresponding Java classes.<br>`all_data_sources`: Dictionary to hold all data sources.<br>`all_defenses_bypassed`: Dictionary to hold all defenses bypassed.<br>`all_platforms`: Dictionary to hold all platforms.<br>`stix_data`: MemoryStore or other stix2 DataSource object holding the domain data.<br>`package_name`: The base package name for the output Java classes.<br>`domain`: The domain of ATT&CK stix_data corresponds to, e.g., "enterprise-attack".<br>`verbose_class`: Whether to include verbose class information, by default False.<br>`output_dir`: The root directory for output, by default ".".
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ other modules in this library under "Additional Modules".

additional_modules/navlayers
additional_modules/attackToExcel
additional_modules/attackToJava
additional_modules/collections
additional_modules/diffStix
1 change: 1 addition & 0 deletions mitreattack/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .attackToExcel import *
from .attackToJava import *
from .navlayers import *
from .collections import *
3 changes: 3 additions & 0 deletions mitreattack/attackToJava/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ATT&CK To Java


Empty file.
162 changes: 162 additions & 0 deletions mitreattack/attackToJava/attackToJava.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""Functions to convert ATT&CK STIX data to Java, as well as entrypoint for attackToJava_cli."""

import argparse
import os
from typing import Dict, List
from sortedcontainers import SortedDict

import pandas as pd
import requests
from loguru import logger
from stix2 import MemoryStore
from pprint import pprint

INVALID_CHARACTERS = ["\\", "/", "*", "[", "]", ":", "?"]
SUB_CHARACTERS = ["\\", "/"]

from mitreattack.attackToExcel import attackToExcel
from mitreattack.attackToJava import stixToJava
from mitreattack.attackToJava import getJavaImports



def export(
version: str = None,
output_dir: str = None,
remote: str = None,
stix_path: str = None,
package_name: str = None,
verbose_class: bool = False,
):
"""Download ATT&CK data from MITRE/CTI and convert it to Java class hierarchy

Parameters
----------
domain : str, optional
The domain of ATT&CK to download, e.g "enterprise-attack", by default "enterprise-attack"
version : str, optional
The version of ATT&CK to download, e.g "v8.1".
If omitted will build the current version of ATT&CK, by default None
output_dir : str, optional
The directory to write the excel files to.
If omitted writes to a subfolder of the current directory depending on specified domain and version, by default "."
remote : str, optional
The URL of a remote ATT&CK Workbench instance to connect to for stix data.
Mutually exclusive with stix_file.
by default None
stix_file : str, optional
Path to a local STIX file containing ATT&CK data for a domain, by default None

Raises
------
ValueError
Raised if both `remote` and `stix_file` are passed
"""

if not package_name:
raise ValueError("Package name needs to be specified")

if not output_dir:
raise ValueError("Output directory needs to be specified")

if output_dir == ".":
raise ValueError("Output directory cannot be the current directory, as the output directoty will be deleted and recreated. Please specify a valid directory")

if remote and stix_path:
raise ValueError("remote and stix_file are mutually exclusive. Please only use one or the other")

#Verify that if stix path is specified it contains JSONs for all three domains
if stix_path:
if os.path.exists(os.path.join(stix_path, "enterprise-attack.json")) and os.path.exists(os.path.join(stix_path, "mobile-attack.json")) and os.path.exists(os.path.join(stix_path, "ics-attack.json")):
pass
else:
raise ValueError("""stix_path must contain JSON files for all three domains: enterprise-attack.json, mobile-attack.json, ics-attack.json.
Use download_attack_stix tool to fetch the files""")

all_data_sources = SortedDict()
all_defenses_bypassed = SortedDict()
all_platforms = SortedDict()

stixToJava.buildOutputDir(package_name=package_name, output_dir=output_dir)

for domain in ["enterprise-attack", "mobile-attack", "ics-attack"]:

logger.info(f"************ Exporting {domain} to To Java ************")

if stix_path:
#Use local files if stix path is specified
mem_store = attackToExcel.get_stix_data(domain=domain, version=version, remote=remote, stix_file=os.path.join(stix_path, f"{domain}.json"))
else:
mem_store = attackToExcel.get_stix_data(domain=domain, version=version, remote=remote)

stixToJava.stixToTactics(stix_data=mem_store, package_name=package_name, domain=domain, verbose_class=verbose_class,output_dir=output_dir)

stixToJava.stixToTechniques(all_data_sources,all_defenses_bypassed,all_platforms,stix_data=mem_store, package_name=package_name, domain=domain, verbose_class=verbose_class,output_dir=output_dir)

logger.info(f"************ Generating import statements for easy use ************")


logger.info(f"************ Running Maven to format and test ************")

with open(os.path.join(output_dir, "imports_example.txt"), "w") as f:
for import_line in getJavaImports.getJavaImports(output_dir,package_name):
f.write(f"{import_line}\n")

stixToJava.runMaven(output_dir=output_dir)





def main():
"""Entrypoint for attackToExcel_cli."""
parser = argparse.ArgumentParser(
description="Download ATT&CK data from MITRE/CTI and convert it to excel spreadsheets"
)

parser.add_argument(
"-version",
type=str,
help="which version of ATT&CK to convert. If omitted, builds the latest version",
)
parser.add_argument(
"-output",
type=str,
required=True,
help="output directory. If omitted writes to a subfolder of the current directory depending on "
"the domain and version",
)
parser.add_argument(
"-remote",
type=str,
default=None,
help="remote url of an ATT&CK workbench server. If omitted, stix data will be acquired from the"
" official ATT&CK Taxii server (cti-taxii.mitre.org)",
)
parser.add_argument(
"-stix-path",
type=str,
default=None,
help="Path to a local directory containing downlaoded STIX filse containing ATT&CK data for all supported domains (enterprise,mobile,ICS) by default None",
)

parser.add_argument(
"-package",
type=str,
default="org.mitre.attack",
help="Java package name from which to start the class hierarchy. If omitted, will use the org.mitre.attack is used",
)

parser.add_argument(
"-verbose",
action="store_true",
help="Populate all fields in Java class, including description and other non-essential. Note this will increase memory usage and file size.",
)
args = parser.parse_args()

export(version=args.version, output_dir=args.output, remote=args.remote, stix_path=args.stix_path, package_name=args.package, verbose_class=args.verbose
)


if __name__ == "__main__":
main()
78 changes: 78 additions & 0 deletions mitreattack/attackToJava/getJavaImports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
print_java_imports.py

This Python script generates Java import statements for all packages in a given directory.
It's useful when you want to import all classes from all packages in a directory.

Usage:
Run the script from the command line with the directory as an argument:
python print_java_imports.py <directory_path>
Replace <directory_path> with the path to the directory for which you want to generate import statements.

Functions:
createImportStatement(package_path):
This function takes a package path as an argument and returns a string that is a Java import statement
for all classes in that package.

print_java_imports(directory):
This function walks through the directory structure of the provided directory and prints an import
statement for each subdirectory.

Error handling:
The script checks if the provided argument is a valid directory. If it's not, it prints an error message
and exits with a status code of 1.

If the script is run without exactly one argument, it prints a usage message and exits with a status code of 1.
"""
import os
import sys

def createImportStatement(package_path):
"""
This function takes a package path as an argument and returns a string that is a Java import statement
for all classes in that package.
"""
return f"import {package_path.replace(os.sep, '.')}.*;"

def getJavaImports(directory, package_name):
"""
This function walks through the directory structure of the provided directory and returns a list of import
statements for each subdirectory.
"""
import_statements = []
for root, dirs, files in os.walk(directory):

# Get the relative path from the directory to the current root
relative_path = os.path.relpath(root, directory)

if package_name not in relative_path.replace(os.sep,"."):
#Skip directories that are not part of the package
continue

#remove everything before the package name, but keep the package name
relative_path = relative_path[relative_path.index(package_name.replace(".",os.sep)):]

# Skip the current directory (.)
if relative_path == ".":
continue

# Create and add the import statement to the list
import_statement = createImportStatement(relative_path)
import_statements.append(import_statement)

return import_statements

if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python print_java_imports.py <directory_path>")
sys.exit(1)

directory_path = sys.argv[1]

if not os.path.isdir(directory_path):
print(f"The path {directory_path} is not a valid directory.")
sys.exit(1)

import_statements = getJavaImports(directory_path)
for statement in import_statements:
print(statement)
Loading