#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # A simple convertor script to generate galaxies from the MITRE NICE framework # https://niccs.cisa.gov/workforce-development/nice-framework # Copyright (C) 2024 Jean-Louis Huynen # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import argparse import json import os import uuid import csv # uuidv4 generated to be concatenated in v5: 1d348708-7cd8-4854-9eac-f93c0dab8cdd parser = argparse.ArgumentParser( description='Create/update the NICE Framework Taxonomies based on the NICE Framework json file.' ) parser.add_argument( "-p", "--path", required=True, help="Path to the NICE Framework json file" ) args = parser.parse_args() if not os.path.exists(args.path): exit("ERROR: path incorrect") if not os.path.exists(os.path.join(os.path.dirname(__file__), "cybersecurity-opm.csv")): exit("ERROR: cannot find opm file") # create OPM description from OPM csv file as this data is missing from the NICE framework with open( os.path.join(os.path.dirname(__file__), "cybersecurity-opm.csv"), newline='' ) as csvfile: opm = {} reader = csv.reader(csvfile, delimiter=',', quotechar="\"") for row in reader: opm[row[0]] = row[1] g = ["work_role", "skill", "task", "knowledge", "competency_area", "opm_code"] galaxies = { "work_role": { "namespace": "nist-nice", "type": "nice-framework-work_roles", "name": "NICE Work Roles", "description": "Work roles based on the NIST NICE framework", "uuid": "10a2e9d7-781b-4ff4-bb3e-f0003108fe41", "version": 1, "icon": 'user', }, "skill": { "namespace": "nist-nice", "type": "nice-framework-skills", "name": "NICE Skills", "description": "Skills based on the NIST NICE framework", "uuid": "96c5b9e7-5e70-479e-990c-8f1dea06c520", "version": 1, "icon": 'user', }, "task": { "namespace": "nist-nice", "type": "nice-framework-tasks", "name": "NICE Tasks", "description": "Tasks based on the NIST NICE framework", "uuid": "98ba1aa3-d171-49e4-adf1-b7fb5e26a942", "version": 1, "icon": 'user', }, "knowledge": { "namespace": "nist-nice", "type": "nice-framework-knowledges", "name": "NICE Knowledges", "description": "Knowledge based on the NIST NICE framework", "uuid": "de7e23f2-cef8-44ed-b209-b584f7da58a2", "version": 1, "icon": 'user', }, "competency_area": { "namespace": "nist-nice", "type": "nice-framework-competency_areas", "name": "NICE Competency areas", "description": "Competency areas based on the NIST NICE framework", "uuid": "e78357aa-01bd-4635-99a1-8eb860fa3bd5", "version": 1, "icon": 'user', }, "opm_code": { "namespace": "nist-nice", "type": "nice-framework-opm_codes", "name": "OPM codes in cybersecurity", "description": "Office of Personnel Management codes in cybersecurity", "uuid": "2c56dfbc-82a5-48db-aea4-854ede951c65", "version": 1, "icon": 'user', }, } clusters = { "work_role": { 'authors': ["NIST", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-work_roles", "name": "NICE Work Roles", "description": "Work roles based on the NIST NICE framework", "uuid": "f81819e1-326b-41a5-89dd-a40d73c5bbbf", 'source': '', 'values': [], 'version': 1, }, "skill": { 'authors': ["NIST", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-skills", "name": "NICE Skills", "description": "Skills based on the NIST NICE framework", "uuid": "2d330f93-fa49-4451-859a-aacc68c63110", 'source': '', 'values': [], 'version': 1, }, "task": { 'authors': ["NIST", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-tasks", "name": "NICE Tasks", "description": "Tasks based on the NIST NICE framework", "uuid": "6bcf78de-a3fb-4636-90bc-95a86817ad65", 'source': '', 'values': [], 'version': 1, }, "knowledge": { 'authors': ["NIST", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-knowledges", "name": "NICE Knowledges", "description": "Knowledge based on the NIST NICE framework", "uuid": "796e3e82-ca9a-4749-8421-4810ed440755", 'source': '', 'values': [], 'version': 1, }, "competency_area": { 'authors': ["NIST", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-competency_areas", "name": "NICE Competency areas", "description": "Competency areas based on the NIST NICE framework", "uuid": "91696bc7-ede9-4875-8814-768bd5c99c66", 'source': '', 'values': [], 'version': 1, }, "opm_code": { 'authors': ["OPM", "Jean-Louis Huynen"], 'category': 'workforce', "type": "nice-framework-opm_codes", "name": "OPM codes in cybersecurity", "description": "Office of Personnel Management codes in cybersecurity", "uuid": "76772dae-0e98-4d96-8603-6993aea936d1", 'source': 'https://dw.opm.gov/datastandards/referenceData/2273/current', 'values': [], 'version': 1, }, } def get_relationships(nice_data, external_id): relationships = [] for element in nice_data["response"]["elements"]["relationships"]: if element["source_element_identifier"] == external_id: relationships.append( { "dest-uuid": str( uuid.uuid5( uuid.UUID("1d348708-7cd8-4854-9eac-f93c0dab8cdd"), element["dest_element_identifier"], ) ), "type": "involves", } ) return relationships with open(args.path) as f: # loading NICE json file nice_data = json.load(f) # relationship counter ctr_rel = 0 # populate clusters' source source = nice_data["response"]["elements"]["documents"] for e in g: if e != "opm_code": clusters[e]["source"] = source[0]["website"] # Populate the clusters' values for element in nice_data["response"]["elements"]["elements"]: # Defining a uuidd v5 identifier uuid_str = str( uuid.uuid5( uuid.UUID("1d348708-7cd8-4854-9eac-f93c0dab8cdd"), element["element_identifier"], ) ) # generating relationship relationships = get_relationships(nice_data, element["element_identifier"]) if relationships != []: ctr_rel = ctr_rel + len(relationships) # Adding values in corresponding cluster if element["element_type"] in g: if element["element_type"] == "opm_code": clusters[element["element_type"]]["values"].append( { "description": opm[element["element_identifier"]], "uuid": uuid_str, "value": f'{opm[element["element_identifier"]][0:150]} - {element["element_identifier"]}', "related": relationships, } ) else: clusters[element["element_type"]]["values"].append( { "description": element["text"], "uuid": uuid_str, "value": element["element_identifier"], "value": f'{element["text"][0:150]} - {element["element_identifier"]}', "related": relationships, } ) # Writing galaxies and clusters for e in g: with open( os.path.join( os.path.dirname(__file__), '..', '..', 'galaxies', f'nice-framework-{e}s.json', ), 'w', ) as f: json.dump(galaxies[e], f, indent=2, sort_keys=True, ensure_ascii=False) f.write( '\n' ) # only needed for the beauty and to be compliant with jq_all_the_things with open( os.path.join( os.path.dirname(__file__), '..', '..', 'clusters', f'nice-framework-{e}s.json', ), 'w', ) as f: json.dump(clusters[e], f, indent=2, sort_keys=True, ensure_ascii=False) f.write( '\n' ) # only needed for the beauty and to be compliant with jq_all_the_things print(f'{len(g)*2} file created:') for e in g: print(f'- nice-framework-{e}s.json contains {len(clusters[e]["values"])} elements') print(f' -{ctr_rel} relationships were created')