Skip to content
Draft
Changes from 2 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
125 changes: 88 additions & 37 deletions utils/inspec_nist_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@
# Notes:
# Hardcoded file paths for input and output

from argparse import ArgumentParser
import json
import logging
import os
import sys
from pprint import pprint
import datetime

LOGFORMAT = os.environ.get("LOGFORMAT", "%(levelname)2s %(message)s")
LOGROOT = os.environ.get("LOGROOT", "root")
LOGLEVEL = os.environ.get("LOGLEVEL", "INFO").upper()
SELF_PATH = os.environ.get("SELF_PATH", os.path.dirname(os.path.abspath(__file__)))
logging.basicConfig(format=LOGFORMAT)
logger = logging.getLogger(LOGROOT)
logger.setLevel(level=LOGLEVEL)


class InSpecMapper():
"""A class to map a MITRE Heimdall project component InSpec profile to other formats"""
Expand Down Expand Up @@ -86,7 +95,7 @@ def map_controls_by_tags(self, tag):

try:
for c in self.controls:
t = c.get('tags').get('nist')[0]
t = c.get('tags').get(tag)[0]
if not tag_map.get(t): tag_map[t] = []
tag_map[t].append(c)

Expand All @@ -110,7 +119,7 @@ def generate_jsonl_statements(self, tag="nist"):
logging.error(str(err))
return []

def generate_combined_statements(self, cmpt_name="My Component", tag="nist"):
def generate_combined_statements(self, cmpt_name="My Component", tag='nist'):
"""Returns JSON object in GovReady "combined" format that can be "oscalized" into a OSCAL component model"""

# Set metadata
Expand Down Expand Up @@ -154,43 +163,85 @@ def generate_combined_statements(self, cmpt_name="My Component", tag="nist"):
logging.error(str(err))
return []

if __name__ == '__main__':
def run():
"""
A utility function to parse runtime arguments and run common InspecMapper
operations in a way that will be callable from the CLI or an additional
Python library.
"""
parser = ArgumentParser(
description="An example Inspec mapper class for mapping into GovReady's 'OSCALized' JSON format.")
parser.add_argument('-i', '--inspec-profile', required=False, dest='inspec_path',
help='Path to the input Inspec profile',
default='heimdall/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile.json')
parser.add_argument('-c', '--component-name', required=False, dest='component_name',
help='The name of the resulting OSCAL component described with Inspec profile.',
default='Ubuntu 16.04 LTS')
parser.add_argument('-t', '--inspec-tag', required=False, dest='inspec_tag',
help='The relevant tag in the Inspec profile that will drive mapping keys.',
default='nist')
parser.add_argument('-o', '--oscal-component', required=False, dest='oscal_component_path',
help='Path for resulting OSCAL component for GovReady based on Inspec profile.',
default='conversions/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile-to-800-53-controls-govready-combined.json')

args, rest = parser.parse_known_args()

logger.debug(f"args: {args}")
logger.debug(f"rest: {rest}")
config = vars(args)

# Collate a component InSpec profile around NIST controls

# Instantiate InSpecMapper for a component
inspec_cmpt = InSpecMapper('heimdall/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile.json')
inspect_cmpt_name = "Ubuntu 16.04 LTS"
inspec_cmpt = InSpecMapper(config.get('inspec_path'))

# Collate the `control` content by NIST 800-53 tags
nist_800_53_tag_map = inspec_cmpt.map_controls_by_tags('nist')

# Hardcoded output file path
converted_path = 'conversions/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile-to-800-53-controls.json'

# Dump conversion to JSON collated around nist tags to file
with open(converted_path, "w") as outfile:
json.dump(nist_800_53_tag_map, outfile, indent=4, sort_keys=True)
print("converted "+inspec_cmpt.profile_path+" to "+converted_path)

# Hardcoded output file path
converted_path = 'conversions/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile-to-800-53-controls-govready-json-l.txt'
# Dump conversion to json-l format that GovReady uses in a oscalizing pipeline to a file
with open(converted_path, "w") as outfile:
# outfile.write(line + '\n' for line in "\nkinspec_cmpt.generate_jsonl_statements('nist'))
for item in inspec_cmpt.generate_jsonl_statements('nist'):
outfile.write(item+"\n")
print("converted "+inspec_cmpt.profile_path+" to "+converted_path)

# Convert to GovReady combined file format, precursor to oscalization
nist_800_53_tag_map = inspec_cmpt.generate_combined_statements(inspect_cmpt_name, 'nist')
# Hardcoded output file path
converted_path = 'conversions/canonical-ubuntu-16.04-lts-stig-baseline-inspec-profile-to-800-53-controls-govready-combined.json'
# Dump conversion to json-l format that GovReady uses in a oscalizing pipeline to a file
with open(converted_path, "w") as outfile:
json.dump(nist_800_53_tag_map, outfile, indent=4, sort_keys=True)
print("converted "+inspec_cmpt.profile_path+" to "+converted_path)



# Here is how to see a mapping
# pprint(nist_800_53_tag_map['AC-10'])
nist_800_53_tag_map = inspec_cmpt.map_controls_by_tags(config.get('inspec_tag'))

filename, extension = os.path.splitext(config.get('oscal_component_path'))

try:
# Hardcoded output file path
converted_path = f"{filename}-control-map{extension}"

# Dump conversion to JSON collated around nist tags to file
with open(converted_path, "w") as outfile:
json.dump(nist_800_53_tag_map, outfile, indent=4, sort_keys=True)
logger.info(f"Converted control map from {inspec_cmpt.profile_path} to {converted_path}")

except Exception as err:
logger.error('Failed to generate control map!')
sys.exit(1)

try:
# Hardcoded output file path
converted_path = f"{filename}-ndjson.txt"
# Dump conversion to json-l format that GovReady uses in a oscalizing pipeline to a file
with open(converted_path, "w") as outfile:
# outfile.write(line + '\n' for line in "\nkinspec_cmpt.generate_jsonl_statements(config.get('inspec_tag')))
for item in inspec_cmpt.generate_jsonl_statements(config.get('inspec_tag')):
outfile.write(item+"\n")

logger.info(f"Converted OSCALized newline-delimited file {inspec_cmpt.profile_path} to {converted_path}")

except Exception as err:
logger.error('Failed to generate OSCALized newline-delimited file')
sys.exit(1)

try:
# Convert to GovReady combined file format, precursor to oscalization
nist_800_53_tag_map = inspec_cmpt.generate_combined_statements(config.get('component_name'), config.get('inspec_tag'))
# Hardcoded output file path
converted_path = config.get('oscal_component_path')
# Dump conversion to json-l format that GovReady uses in a oscalizing pipeline to a file
with open(converted_path, "w") as outfile:
json.dump(nist_800_53_tag_map, outfile, indent=4, sort_keys=True)
logger.info(f"Converted final combined component from {inspec_cmpt.profile_path} to {converted_path}")

except Exception as err:
logger.error('Failed to generate final combined component')
sys.exit(1)


if __name__ == '__main__':
run()