From 0e7a273794adbe8974fee6db0bbdf437106c7d29 Mon Sep 17 00:00:00 2001 From: kemley76 Date: Thu, 6 Jun 2024 17:54:06 -0400 Subject: [PATCH 1/6] input validation for checklist metadata Signed-off-by: kemley76 --- src/commands/convert/hdf2ckl.ts | 19 +++++++++++++++---- src/commands/generate/ckl_metadata.ts | 12 ++++++++++-- src/resources/files.json | 2 +- src/types/checklist.d.ts | 6 +++--- 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/commands/convert/hdf2ckl.ts b/src/commands/convert/hdf2ckl.ts index 5c74fef7e..eee9ba59a 100644 --- a/src/commands/convert/hdf2ckl.ts +++ b/src/commands/convert/hdf2ckl.ts @@ -8,6 +8,7 @@ import Mustache from 'mustache' import {CKLMetadata} from '../../types/checklist' import {convertFullPathToFilename, getProfileInfo} from '../../utils/global' import {getDetails} from '../../utils/checklist' +import {validateChecklistMetadata} from '@mitre/hdf-converters' export default class HDF2CKL extends Command { static usage = 'convert hdf2ckl -i -o [-h] [-m ] [-H ] [-F ] [-M ] [-I ]' @@ -46,10 +47,14 @@ export default class HDF2CKL extends Command { stigid: profileName || null, role: 'None', type: 'Computing', - hostname: flags.hostname || _.get(contextualizedEvaluation, 'evaluation.data.passthrough.hostname') || null, - ip: flags.ip || _.get(contextualizedEvaluation, 'evaluation.data.passthrough.ip') || null, - mac: flags.mac || _.get(contextualizedEvaluation, 'evaluation.data.passthrough.mac') || null, - fqdn: flags.fqdn || _.get(contextualizedEvaluation, 'evaluation.data.passthrough.fqdn') || null, + hostname: flags.hostname || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostname') || + _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostname') || null, + hostip: flags.ip || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostip') || + _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostip') || null, + hostmac: flags.mac || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostmac') || + _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostmac') || null, + hostfqdn: flags.fqdn || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostfqdn') || + _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostfqdn') || null, tech_area: null, target_key: '0', web_or_database: 'false', @@ -66,6 +71,12 @@ export default class HDF2CKL extends Command { } } + const validationResults = validateChecklistMetadata(cklMetadata) + if (validationResults.isError) { + console.error(`Cannot create checklist file:\n${validationResults.message}`) + process.exit(1) + } + cklData = { releaseInfo: cklMetadata.benchmark.plaintext, ...cklMetadata, diff --git a/src/commands/generate/ckl_metadata.ts b/src/commands/generate/ckl_metadata.ts index 3ca468fba..8ebfe7c39 100644 --- a/src/commands/generate/ckl_metadata.ts +++ b/src/commands/generate/ckl_metadata.ts @@ -2,6 +2,7 @@ import {Command, Flags} from '@oclif/core' import fs from 'fs' import promptSync from 'prompt-sync' import _ from 'lodash' +import {validateChecklistMetadata} from '@mitre/hdf-converters' const prompt = promptSync() @@ -30,14 +31,21 @@ export default class GenerateCKLMetadata extends Command { role: prompt({ask: 'What is the computing role? (None/Workstation/Member Server/Domain Controller) '}) || null, type: _.capitalize(prompt({ask: 'What is the asset type? (Computing/Non-Computing) '})) || null, hostname: prompt({ask: 'What is the asset hostname? '}) || null, - ip: prompt({ask: 'What is the asset IP address? '}) || null, - mac: prompt({ask: 'What is the asset MAC address? '}) || null, + hostip: prompt({ask: 'What is the asset IP address? '}) || null, + hostmac: prompt({ask: 'What is the asset MAC address? '}) || null, + hostfqdn: prompt({ask: 'What is the asset FQDN? '}) || null, tech_area: prompt({ask: 'What is the tech area? (Application Review/Boundary Security/CDS Admin Review/CDS Technical Review/Database Review/Domain Name System (DNS)/Exchange Server/Host Based System Security (HBSS)/Internal Network/Mobility/Releasable Networks (REL)/Releaseable Networks (REL)/Traditional Security/UNIX OS/VVOIP Review/Web Review/Windows OS/Other Review) '}) || null, // Yes, these typos really are how the enumerations are defined in STIG viewer's source code target_key: prompt({ask: 'What is the target key? '}) || null, web_or_database: prompt({ask: 'Is the target a web or database? (y/n) '}).toLowerCase() === 'y', web_db_site: prompt({ask: 'What is the Web or DB site? '}) || null, web_db_instance: prompt({ask: 'What is the Web or DB instance? '}) || null, } + const validationResults = validateChecklistMetadata(cklMetadata) + if (validationResults.isError) { + console.error(`Unable to generate checklist metadata:\n${validationResults.message}`) + process.exit(1) + } + fs.writeFileSync(flags.output, JSON.stringify(cklMetadata)) } } diff --git a/src/resources/files.json b/src/resources/files.json index 98b5b8f15..6f68d9e52 100644 --- a/src/resources/files.json +++ b/src/resources/files.json @@ -13,7 +13,7 @@ }, "cklExport.ckl": { "type": "string", - "data": "\r\n\r\n\r\n\t\r\n\t\t{{role}}<\/ROLE>\r\n\t\t{{type}}<\/ASSET_TYPE>\r\n\t\t{{hostname}}<\/HOST_NAME>\r\n\t\t{{ip}}<\/HOST_IP>\r\n\t\t{{mac}}<\/HOST_MAC>\r\n\t\t{{fqdn}}<\/HOST_FQDN>\r\n\t\t<\/TARGET_COMMENT>\r\n\t\t{{tech_area}}<\/TECH_AREA>\r\n\t\t{{target_key}}<\/TARGET_KEY>\r\n\t\t{{web_or_database}}<\/WEB_OR_DATABASE>\r\n\t\t{{web_db_site}}<\/WEB_DB_SITE>\r\n\t\t{{web_db_instance}}<\/WEB_DB_INSTANCE>\r\n\t<\/ASSET>\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tversion<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.version}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tclassification<\/SID_NAME>\r\n\t\t\t\t\tUNCLASSIFIED<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tcustomname<\/SID_NAME>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tstigid<\/SID_NAME>\r\n\t\t\t\t\t{{stigid}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tdescription<\/SID_NAME>\r\n\t\t\t\t\t{{profileInfo}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tfilename<\/SID_NAME>\r\n\t\t\t\t\t{{fileName}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\treleaseinfo<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.plaintext}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\ttitle<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.title}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tuuid<\/SID_NAME>\r\n\t\t\t\t\t{{uuid}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tnotice<\/SID_NAME>\r\n\t\t\t\t\tterms-of-use<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tsource<\/SID_NAME>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t<\/STIG_INFO>\r\n\t\t\t{{#controls}}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tVuln_Num<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{vid}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSeverity<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{severity}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tGroup_Title<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{gtitle}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{rid}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_Ver<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{ruleVersion}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_Title<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{title}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tVuln_Discuss<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{description}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tIA_Controls<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tCheck_Content<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{checkText}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFix_Text<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{fixText}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFalse_Positives<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFalse_Negatives<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tDocumentable<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\tfalse<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tMitigations<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tPotential_Impact<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tThird_Party_Tools<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tMitigation_Control<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tResponsibility<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSecurity_Override_Guidance<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tCheck_Content_Ref<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\tM<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tWeight<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t10.0<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tClass<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{classification}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSTIGRef<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{profileName}} {{startTime}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tTargetKey<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{target_key}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSTIG_UUID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{uuidV4}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tLEGACY_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tLEGACY_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t{{#ccis}}\r\n\t\t\t\t\r\n\t\t\t\t\tCCI_REF<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{.}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t{{\/ccis}}\r\n\t\t\t\t{{status}}<\/STATUS>\r\n\t\t\t\t{{results}}<\/FINDING_DETAILS>\r\n\t\t\t\t<\/COMMENTS>\r\n\t\t\t\t<\/SEVERITY_OVERRIDE>\r\n\t\t\t\t<\/SEVERITY_JUSTIFICATION>\r\n\t\t\t<\/VULN>\r\n\t\t\t{{\/controls}}\r\n\t\t<\/iSTIG>\r\n\t<\/STIGS>\r\n<\/CHECKLIST>" + "data": "\r\n\r\n\r\n\t\r\n\t\t{{role}}<\/ROLE>\r\n\t\t{{type}}<\/ASSET_TYPE>\r\n\t\t{{hostname}}<\/HOST_NAME>\r\n\t\t{{hostip}}<\/HOST_IP>\r\n\t\t{{hostmac}}<\/HOST_MAC>\r\n\t\t{{hostfqdn}}<\/HOST_FQDN>\r\n\t\t<\/TARGET_COMMENT>\r\n\t\t{{tech_area}}<\/TECH_AREA>\r\n\t\t{{target_key}}<\/TARGET_KEY>\r\n\t\t{{web_or_database}}<\/WEB_OR_DATABASE>\r\n\t\t{{web_db_site}}<\/WEB_DB_SITE>\r\n\t\t{{web_db_instance}}<\/WEB_DB_INSTANCE>\r\n\t<\/ASSET>\r\n\t\r\n\t\t\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tversion<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.version}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tclassification<\/SID_NAME>\r\n\t\t\t\t\tUNCLASSIFIED<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tcustomname<\/SID_NAME>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tstigid<\/SID_NAME>\r\n\t\t\t\t\t{{stigid}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tdescription<\/SID_NAME>\r\n\t\t\t\t\t{{profileInfo}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tfilename<\/SID_NAME>\r\n\t\t\t\t\t{{fileName}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\treleaseinfo<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.plaintext}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\ttitle<\/SID_NAME>\r\n\t\t\t\t\t{{benchmark.title}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tuuid<\/SID_NAME>\r\n\t\t\t\t\t{{uuid}}<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tnotice<\/SID_NAME>\r\n\t\t\t\t\tterms-of-use<\/SID_DATA>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tsource<\/SID_NAME>\r\n\t\t\t\t<\/SI_DATA>\r\n\t\t\t<\/STIG_INFO>\r\n\t\t\t{{#controls}}\r\n\t\t\t\r\n\t\t\t\t\r\n\t\t\t\t\tVuln_Num<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{vid}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSeverity<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{severity}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tGroup_Title<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{gtitle}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{rid}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_Ver<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{ruleVersion}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tRule_Title<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{title}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tVuln_Discuss<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{description}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tIA_Controls<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tCheck_Content<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{checkText}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFix_Text<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{fixText}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFalse_Positives<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tFalse_Negatives<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tDocumentable<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\tfalse<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tMitigations<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tPotential_Impact<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tThird_Party_Tools<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tMitigation_Control<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tResponsibility<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSecurity_Override_Guidance<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tCheck_Content_Ref<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\tM<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tWeight<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t10.0<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tClass<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{classification}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSTIGRef<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{profileName}} {{startTime}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tTargetKey<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{target_key}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tSTIG_UUID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{uuidV4}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tLEGACY_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t\r\n\t\t\t\t\tLEGACY_ID<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t{{#ccis}}\r\n\t\t\t\t\r\n\t\t\t\t\tCCI_REF<\/VULN_ATTRIBUTE>\r\n\t\t\t\t\t{{.}}<\/ATTRIBUTE_DATA>\r\n\t\t\t\t<\/STIG_DATA>\r\n\t\t\t\t{{\/ccis}}\r\n\t\t\t\t{{status}}<\/STATUS>\r\n\t\t\t\t{{results}}<\/FINDING_DETAILS>\r\n\t\t\t\t<\/COMMENTS>\r\n\t\t\t\t<\/SEVERITY_OVERRIDE>\r\n\t\t\t\t<\/SEVERITY_JUSTIFICATION>\r\n\t\t\t<\/VULN>\r\n\t\t\t{{\/controls}}\r\n\t\t<\/iSTIG>\r\n\t<\/STIGS>\r\n<\/CHECKLIST>" }, "AttestationTemplate": { "type": "b64file", diff --git a/src/types/checklist.d.ts b/src/types/checklist.d.ts index 996363c61..c721f6362 100644 --- a/src/types/checklist.d.ts +++ b/src/types/checklist.d.ts @@ -30,9 +30,9 @@ export interface CKLMetadata { role: string | null; type: string | null; hostname: string | null; - ip: string | null; - mac: string | null; - fqdn: string | null; + hostip: string | null; + hostmac: string | null; + hostfqdn: string | null; tech_area: string | null; target_key: string | null; web_or_database: string | null; From 0d5fd2cff9669473e6c434fb71eb6a96c52aa54d Mon Sep 17 00:00:00 2001 From: kemley76 Date: Fri, 7 Jun 2024 13:49:01 -0400 Subject: [PATCH 2/6] use hdf-converters in hdf2ckl Signed-off-by: kemley76 --- src/commands/convert/ckl2hdf.ts | 9 ++- src/commands/convert/hdf2ckl.ts | 83 ++++++++------------------- src/commands/generate/ckl_metadata.ts | 18 +++--- src/resources/files.json | 4 -- src/types/checklist.d.ts | 36 ++++++------ 5 files changed, 57 insertions(+), 93 deletions(-) diff --git a/src/commands/convert/ckl2hdf.ts b/src/commands/convert/ckl2hdf.ts index 1851673fb..c289cdb77 100644 --- a/src/commands/convert/ckl2hdf.ts +++ b/src/commands/convert/ckl2hdf.ts @@ -23,7 +23,12 @@ export default class CKL2HDF extends Command { const data = fs.readFileSync(flags.input, 'utf8') checkInput({data, filename: flags.input}, 'checklist', 'DISA Checklist') - const converter = new Mapper(data, flags['with-raw']) - fs.writeFileSync(checkSuffix(flags.output), JSON.stringify(converter.toHdf())) + try { + const converter = new Mapper(data, flags['with-raw']) + fs.writeFileSync(checkSuffix(flags.output), JSON.stringify(converter.toHdf())) + } catch (error) { + console.error(`Error converting to hdf:\n${error}`) + process.exit(1) + } } } diff --git a/src/commands/convert/hdf2ckl.ts b/src/commands/convert/hdf2ckl.ts index eee9ba59a..8095be953 100644 --- a/src/commands/convert/hdf2ckl.ts +++ b/src/commands/convert/hdf2ckl.ts @@ -1,14 +1,8 @@ import {Command, Flags} from '@oclif/core' -import {contextualizeEvaluation} from 'inspecjs' import _ from 'lodash' import fs from 'fs' -import {v4} from 'uuid' -import {default as files} from '../../resources/files.json' -import Mustache from 'mustache' import {CKLMetadata} from '../../types/checklist' -import {convertFullPathToFilename, getProfileInfo} from '../../utils/global' -import {getDetails} from '../../utils/checklist' -import {validateChecklistMetadata} from '@mitre/hdf-converters' +import {ChecklistResults as Mapper} from '@mitre/hdf-converters' export default class HDF2CKL extends Command { static usage = 'convert hdf2ckl -i -o [-h] [-m ] [-H ] [-F ] [-M ] [-I ]' @@ -26,64 +20,33 @@ export default class HDF2CKL extends Command { ip: Flags.string({char: 'I', required: false, description: 'IP address for CKL metadata'}), } - static examples = ['saf convert hdf2ckl -i rhel7-results.json -o rhel7.ckl --fqdn reverseproxy.example.org --hostname reverseproxy --ip 10.0.0.3 --mac 12:34:56:78:90'] + static examples = ['saf convert hdf2ckl -i rhel7-results.json -o rhel7.ckl --fqdn reverseproxy.example.org --hostname reverseproxy --ip 10.0.0.3 --mac 12:34:56:78:90:AB'] async run() { const {flags} = await this.parse(HDF2CKL) - const contextualizedEvaluation = contextualizeEvaluation(JSON.parse(fs.readFileSync(flags.input, 'utf8'))) - const profileName = contextualizedEvaluation.data.profiles[0].name - const controls = contextualizedEvaluation.contains.flatMap(profile => profile.contains) || [] - const rootControls = _.uniqBy(controls, control => - _.get(control, 'root.hdf.wraps.id'), - ).map(({root}) => root) - let cklData = {} - const cklMetadata: CKLMetadata = { - fileName: convertFullPathToFilename(flags.input), - benchmark: { - title: profileName || null, - version: '1', - plaintext: null, - }, - stigid: profileName || null, - role: 'None', - type: 'Computing', - hostname: flags.hostname || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostname') || - _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostname') || null, - hostip: flags.ip || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostip') || - _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostip') || null, - hostmac: flags.mac || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostmac') || - _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostmac') || null, - hostfqdn: flags.fqdn || _.get(contextualizedEvaluation, 'data.passthrough.checklist.asset.hostfqdn') || - _.get(contextualizedEvaluation, 'data.passthrough.metadata.hostfqdn') || null, - tech_area: null, - target_key: '0', - web_or_database: 'false', - web_db_site: null, - web_db_instance: null, - } - - if (flags.metadata) { - const cklMetadataInput: CKLMetadata = JSON.parse(fs.readFileSync(flags.metadata, 'utf8')) - for (const field in cklMetadataInput) { - if (typeof cklMetadata[field] === 'string' || typeof cklMetadata[field] === 'object') { - cklMetadata[field] = cklMetadataInput[field] - } - } - } - const validationResults = validateChecklistMetadata(cklMetadata) - if (validationResults.isError) { - console.error(`Cannot create checklist file:\n${validationResults.message}`) + /* Order of prescedece for checklist metadata: + command flags (hostname, ip, etc.) + metadata flag + input hdf file passthrough.metadata + input hdf file passthrough.checklist.asset */ + + const defaultMetadata: CKLMetadata = {role: 'None', assettype: 'Computing', targetkey: '0', webordatabase: false, profiles: []} + const inputHDF = JSON.parse(fs.readFileSync(flags.input, 'utf8')) + const flagMetadata = {hostname: flags.hostname, hostip: flags.ip, hostmac: flags.mac, hostfqdn: flags.fqdn} + const fileMetadata = flags.metadata ? JSON.parse(fs.readFileSync(flags.metadata, 'utf8')) : {} + const hdfMetadata = _.get(inputHDF, 'passthrough.metadata', _.get(inputHDF, 'passthrough.checklist.asset', {})) + const metadata = _.merge(_.merge(defaultMetadata, hdfMetadata, fileMetadata, flagMetadata)) + + // use the input hdf's profiles by default since they cant be included elsewhere + metadata.profiles = _.get(hdfMetadata, 'profiles', []) + _.set(inputHDF, 'passthrough.metadata', metadata) + + try { + fs.writeFileSync(flags.output, new Mapper(inputHDF).toCkl()) + } catch (error) { + console.error(`Error creating checklist:\n${error}`) process.exit(1) } - - cklData = { - releaseInfo: cklMetadata.benchmark.plaintext, - ...cklMetadata, - profileInfo: getProfileInfo(contextualizedEvaluation, cklMetadata.fileName), - uuid: v4(), - controls: rootControls.map(control => getDetails(control, profileName)), - } - fs.writeFileSync(flags.output, Mustache.render(files['cklExport.ckl'].data, cklData).replaceAll(/[^\x00-\x7F]/g, '')) } } diff --git a/src/commands/generate/ckl_metadata.ts b/src/commands/generate/ckl_metadata.ts index 8ebfe7c39..efb00fb1f 100644 --- a/src/commands/generate/ckl_metadata.ts +++ b/src/commands/generate/ckl_metadata.ts @@ -27,18 +27,20 @@ export default class GenerateCKLMetadata extends Command { version: prompt({ask: 'What is the benchmark version? '}) || null, plaintext: prompt({ask: 'What is the notes for release info? '}) || null, }, - stigid: prompt({ask: 'What is the STIG ID? '}) || null, - role: prompt({ask: 'What is the computing role? (None/Workstation/Member Server/Domain Controller) '}) || null, - type: _.capitalize(prompt({ask: 'What is the asset type? (Computing/Non-Computing) '})) || null, + marking: prompt({ask: 'What is the marking? '}) || null, hostname: prompt({ask: 'What is the asset hostname? '}) || null, hostip: prompt({ask: 'What is the asset IP address? '}) || null, hostmac: prompt({ask: 'What is the asset MAC address? '}) || null, hostfqdn: prompt({ask: 'What is the asset FQDN? '}) || null, - tech_area: prompt({ask: 'What is the tech area? (Application Review/Boundary Security/CDS Admin Review/CDS Technical Review/Database Review/Domain Name System (DNS)/Exchange Server/Host Based System Security (HBSS)/Internal Network/Mobility/Releasable Networks (REL)/Releaseable Networks (REL)/Traditional Security/UNIX OS/VVOIP Review/Web Review/Windows OS/Other Review) '}) || null, // Yes, these typos really are how the enumerations are defined in STIG viewer's source code - target_key: prompt({ask: 'What is the target key? '}) || null, - web_or_database: prompt({ask: 'Is the target a web or database? (y/n) '}).toLowerCase() === 'y', - web_db_site: prompt({ask: 'What is the Web or DB site? '}) || null, - web_db_instance: prompt({ask: 'What is the Web or DB instance? '}) || null, + targetcomment: prompt({ask: 'What are the target comments? '}) || null, + role: prompt({ask: 'What is the computing role? (None/Workstation/Member Server/Domain Controller) '}) || null, + assettype: _.capitalize(prompt({ask: 'What is the asset type? (Computing/Non-Computing) '})) || null, + techarea: prompt({ask: 'What is the tech area? (Application Review/Boundary Security/CDS Admin Review/CDS Technical Review/Database Review/Domain Name System (DNS)/Exchange Server/Host Based System Security (HBSS)/Internal Network/Mobility/Releasable Networks (REL)/Releaseable Networks (REL)/Traditional Security/UNIX OS/VVOIP Review/Web Review/Windows OS/Other Review) '}) || null, // Yes, these typos really are how the enumerations are defined in STIG viewer's source code + stigguid: prompt({ask: 'What is the STIG ID? '}) || null, + targetkey: prompt({ask: 'What is the target key? '}) || null, + webordatabase: prompt({ask: 'Is the target a web or database? (y/n) '}).toLowerCase() === 'y', + webdbsite: prompt({ask: 'What is the Web or DB site? '}) || null, + webdbinstance: prompt({ask: 'What is the Web or DB instance? '}) || null, } const validationResults = validateChecklistMetadata(cklMetadata) if (validationResults.isError) { diff --git a/src/resources/files.json b/src/resources/files.json index 6f68d9e52..46c027abd 100644 --- a/src/resources/files.json +++ b/src/resources/files.json @@ -11,10 +11,6 @@ "type": "string", "data": "\n draft\n {{Benchmark.title}}\n HDF Results Set converted Into XCCDF Benchmark\n \n {{Benchmark.passthrough}}\n \n {{Benchmark.version}}\n \n {{Benchmark.metadata.copyright}}\n {{Benchmark.metadata.maintainer}}\n \n {{#Benchmark.Profile}}\n \n draft\n {{version}}\n {{title}}\n {{#select}}\n