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

Add convert CCI list workflow #6336

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
938fe8f
Fix typo
jtquach1 Oct 24, 2024
dac8ae5
Fix crashing on CCIs who had no references to any NIST controls
jtquach1 Oct 28, 2024
a5769ce
Generate CCI definitions alongside CCI to NIST file
jtquach1 Oct 28, 2024
da54c1f
Rename xml2json to cciListXml2json
jtquach1 Oct 28, 2024
8694abd
Update CciNistMappingData with converted content from U_CCI_List.xml
jtquach1 Oct 28, 2024
a4f7474
Update frontend component, CciNistMapping used in converters, delete …
jtquach1 Oct 28, 2024
9b02a5d
Add comment about GitHub-hosted wiki for cciListXml2json
jtquach1 Oct 28, 2024
cbb1d95
Create GitHub workflow for pulling down U_CCI_List.xml and converting…
jtquach1 Oct 28, 2024
4db826c
Fix cron syntax
jtquach1 Oct 28, 2024
8e23f3a
Also add Prettier job to format CciNistMappingData.ts
jtquach1 Oct 28, 2024
862d9a7
Update author and email in commit changes step
jtquach1 Oct 29, 2024
5089280
Update XCCDF mapper OpenSCAP ubuntu1804 expected JSON
jtquach1 Oct 29, 2024
f4701a8
Remove temporary file after updating CciNistMappingData.ts
jtquach1 Oct 29, 2024
1c8b3c0
Address review comments
jtquach1 Oct 30, 2024
d2fe18d
Check that workflow runs
jtquach1 Oct 30, 2024
7f7a35d
Remove step
jtquach1 Oct 30, 2024
b06002d
Update xml-file parameter in publish-date step
jtquach1 Oct 30, 2024
fe6d525
Add namespaces parameter to publish-date step and add debug commands
jtquach1 Oct 30, 2024
f96cb75
Update xpath
jtquach1 Oct 30, 2024
aa97891
Try to fix xpath again
jtquach1 Nov 1, 2024
755c9f7
Try to fix path of input XML
jtquach1 Nov 1, 2024
967cf1d
Try to resolve 'a mapping was not expected' error
jtquach1 Nov 1, 2024
cdf6ba1
Remove env var
jtquach1 Nov 1, 2024
8d9ac8c
Set env var with path to input XML in separate step
jtquach1 Nov 1, 2024
51dd061
Update paths to input and output XML files
jtquach1 Nov 1, 2024
1f4320c
Move env attribute
jtquach1 Nov 1, 2024
36b7e54
Try to print result of previous step
jtquach1 Nov 1, 2024
2ab6d26
Try to grab output of publish-date step from object
jtquach1 Nov 1, 2024
9fc1587
Update to proper branch
jtquach1 Nov 1, 2024
915c74e
Explicitly specify parent-most component in U_CCI_List.xml to grab ve…
jtquach1 Nov 1, 2024
b737264
Move getCCIsForNISTTags function into libs/hdf-converters/src/mapping…
jtquach1 Nov 1, 2024
af2190f
Update GitHub Action and cciListXml2json script to create NIST->CCI J…
jtquach1 Nov 1, 2024
4199203
Update files from cciListXml2json script and new formatting
jtquach1 Nov 1, 2024
a8b6777
Remove step
jtquach1 Nov 1, 2024
127455a
Fix env var
jtquach1 Nov 1, 2024
7961bcc
Remove trailing slash
jtquach1 Nov 1, 2024
e4baee0
Restructure CCI to NIST and NIST to CCI logic across mappers and dele…
jtquach1 Nov 5, 2024
e420048
Export variables representing magic strings to be used in NIST2CCI fu…
jtquach1 Nov 5, 2024
41a577c
Update path for output directory and rename steps
jtquach1 Nov 5, 2024
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
62 changes: 62 additions & 0 deletions .github/workflows/convert-cci-list.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Convert CCI List XML to JSON

on:
push:
# branches: ['master']
branches: ['add-convert-cci-list-workflow']

# Run this workflow on the 1st day at 00:00 every month
# schedule:
# - cron: '0 0 1 * *'

env:
# This URL is super brittle with how links constantly get changed.
CCI_LIST_ZIP_URL: https://dl.dod.cyber.mil/wp-content/uploads/stigs/zip/U_CCI_List.zip

jobs:
convert-cci-list:
runs-on: ubuntu-22.04
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
check-latest: true
cache: 'yarn'

- name: Install project dependencies
run: yarn install --frozen-lockfile

- name: Download CCI List
run: |
curl -o U_CCI_List.zip $CCI_LIST_ZIP_URL && unzip U_CCI_List.zip

- name: Get publish date of CCI List
id: publish-date
uses: mavrosxristoforos/[email protected]
with:
xml-file: 'U_CCI_List.xml'
xpath: '/*[local-name()="cci_list"]/*[local-name()="metadata"]/*[local-name()="publishdate"]'
namespaces: '{"ns": "http://iase.disa.mil/cci"}'

- name: Set directory environment variables for next step
run: |
echo "ROOT_DIRECTORY=$(pwd)" >> $GITHUB_ENV
echo "OUTPUT_DIRECTORY=$(pwd)/libs/hdf-converters/src/mappings" >> $GITHUB_ENV

- name: Convert CCI List XML to CCI->NIST, CCI->Definitions, and NIST->CCI JSON files
run: yarn workspace @mitre/hdf-converters cciListXml2json -i $ROOT_DIRECTORY/U_CCI_List.xml -n $OUTPUT_DIRECTORY/U_CCI_List.nist.json -d $OUTPUT_DIRECTORY/U_CCI_List.defs.json -c $OUTPUT_DIRECTORY/U_CCI_List.cci.json

- name: Commit changes to produced JSON files
# run: |
# git config --local user.email "[email protected]"
# git config --local user.name "MITRE SAF Automation"
# git add $OUTPUT_DIRECTORY/U_CCI_List.nist.json $OUTPUT_DIRECTORY/U_CCI_List.defs.json $OUTPUT_DIRECTORY/U_CCI_List.cci.json
# git commit -sm "Update CCI List to the current NIST and definition mappings as of $DATETIME"
# git push
run: echo $DATETIME && git status
env:
DATETIME: ${{steps.publish-date.outputs.info}}
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,12 @@
<script lang="ts">
import ResponsiveRowSwitch from '@/components/cards/controltable/ResponsiveRowSwitch.vue';
import HtmlSanitizeMixin from '@/mixins/HtmlSanitizeMixin';
import {CCI_DESCRIPTIONS} from '@/utilities/cci_util';
import {getControlRunTime} from '@/utilities/delta_util';
import {nistCanonConfig, NIST_DESCRIPTIONS} from '@/utilities/nist_util';
import {
CCI_TO_DEFINITION,
CCI_TO_NIST
} from '@mitre/hdf-converters/src/mappings/CciNistMappingData';
import {ContextualizedControl, is_control, parse_nist} from 'inspecjs';
import * as _ from 'lodash';
import Component, {mixins} from 'vue-class-component';
Expand Down Expand Up @@ -224,8 +227,11 @@ export default class ControlRowHeader extends mixins(HtmlSanitizeMixin) {
if (found) {
return found;
}
} else if (CCI_DESCRIPTIONS[tag.toUpperCase()]) {
return CCI_DESCRIPTIONS[tag.toUpperCase()].def;
} else if (
CCI_TO_NIST[tag.toUpperCase()] &&
CCI_TO_DEFINITION[tag.toUpperCase()]
) {
return CCI_TO_DEFINITION[tag.toUpperCase()];
}
return 'Unrecognized Tag';
}
Expand Down
14,210 changes: 0 additions & 14,210 deletions apps/frontend/src/utilities/cci_util.ts

This file was deleted.

147 changes: 147 additions & 0 deletions libs/hdf-converters/data/converters/cciListXml2json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import fs from 'fs';
import * as _ from 'lodash';
import xml2js from 'xml2js';
import {parseArgs} from 'node:util';

// Documentation is located at https://github.com/mitre/heimdall2/wiki/Control-Correlation-Identifier-(CCI)-Converter.
const parser = new xml2js.Parser();

const options = {
input: {
type: 'string',
short: 'i'
},
cci2nist: {
type: 'string',
short: 'n'
},
cci2definitions: {
type: 'string',
short: 'd'
},
nist2cci: {
type: 'string',
short: 'c'
}
} as const;

// XML Structure after conversion
export interface ICCIList {
cci_list: {
cci_items: {
cci_item: {
$: Record<string, string>;
references?: {
reference: {
$: Record<string, string>;
}[];
}[];
definition: string[];
}[];
}[];
};
}

// Check that we're not doing `npm test`; it will look for the arguments to the input and output files.
const scriptIsCalled = process.argv[1].includes('cciListXml2json');

if (scriptIsCalled) {
const {values} = parseArgs({options});

const pathToInfile = values.input;
const pathToCci2NistOutfile = values.cci2nist;
const pathToCci2DefinitionsOutfile = values.cci2definitions;
const pathToNist2CciOutfile = values.nist2cci;

if (
!pathToInfile ||
!pathToCci2NistOutfile ||
!pathToCci2DefinitionsOutfile ||
!pathToNist2CciOutfile
) {
console.error(
'You must provide the path to the input and three output files.'
);
} else {
fs.readFile(pathToInfile, function (readFileError, data) {
if (readFileError) {
console.error(`Failed to read ${pathToInfile}: ${readFileError}`);
} else {
// Parse XML to JS Object
parser.parseString(data, (parseFileError: any, converted: ICCIList) => {
if (parseFileError) {
console.error(`Failed to parse ${pathToInfile}: ${parseFileError}`);
} else {
// These store our CCI->NIST names, CCI->definitions, and NIST->CCI mappings
const nists: Record<string, string> = {};
const definitions: Record<string, string> = {};
const ccis: Record<string, string[]> = {};

// For all CCI items
for (const cciItem of converted.cci_list.cci_items[0].cci_item) {
// Get the latest reference
const newestReference = _.maxBy(
cciItem.references?.[0].reference,
(item) => _.get(item, '$.version')
);
if (newestReference) {
nists[cciItem.$.id] = newestReference.$.index;
if (ccis[newestReference.$.index] === undefined) {
ccis[newestReference.$.index] = [cciItem.$.id];
} else {
ccis[newestReference.$.index].push(cciItem.$.id);
}
definitions[cciItem.$.id] = cciItem.definition[0];
} else {
console.error(`No NIST Controls found for ${cciItem.$.id}`);
}
}
fs.writeFileSync(
pathToCci2NistOutfile,
JSON.stringify(nists, null, 2)
);
fs.writeFileSync(
pathToCci2DefinitionsOutfile,
JSON.stringify(definitions, null, 2)
);
fs.writeFileSync(
pathToNist2CciOutfile,
JSON.stringify(unflatten(ccis), null, 2)
);
}
});
}
});
}
}

export const CCIS_KEY = 'ccis';
export const DELIMITER = ' ';

type Leaf = {
[CCIS_KEY]?: string[];
};

type Branch = Leaf & {
[key: string]: Branch | string[] | undefined;
};

export type Trie = {
[key: string]: Branch;
};

export function removeParentheses(key: string): string {
return key.replace(/[()]/g, '');
}

function unflatten(fullNistPathToListOfCcis: Record<string, string[]>): Trie {
const result = {};
const keys = _.keys(fullNistPathToListOfCcis);
for (const key of keys) {
const path = key.split(DELIMITER).map(removeParentheses);
path.push(CCIS_KEY);
const value = fullNistPathToListOfCcis[key];
_.setWith(result, path, value, Object);
}
return result;
}
57 changes: 0 additions & 57 deletions libs/hdf-converters/data/converters/xml2json.ts

This file was deleted.

2 changes: 1 addition & 1 deletion libs/hdf-converters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"postpack:win32": "move package.json.orig package.json",
"test": "jest",
"csv2json": "tsx data/converters/csv2json.ts",
"xml2json": "tsx data/converters/xml2json.ts"
"cciListXml2json": "tsx data/converters/cciListXml2json.ts"
},
"dependencies": {
"@aws-sdk/client-config-service": "^3.95.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"platform": {
"name": "Heimdall Tools",
"release": "2.8.1",
"release": "2.10.20",
"target_id": "cpe:/o:canonical:ubuntu_linux:18.04::~~lts~~~"
},
"version": "2.8.1",
"version": "2.10.20",
"statistics": {},
"profiles": [
{
Expand Down Expand Up @@ -90585,7 +90585,7 @@
"CCI-001495"
],
"nist": [
"AU-9"
"AU-9 a"
],
"severity": "medium",
"description": "/bin\n/sbin\n/usr/bin\n/usr/sbin\n/usr/local/bin\n/usr/local/sbin\nAll these directories should be owned by the root user.\nIf any directory DIR in these directories is found\nto be owned by a user other than root, correct its ownership with the\nfollowing command:\n$ sudo chown root DIR",
Expand Down Expand Up @@ -90744,7 +90744,7 @@
"CCI-001495"
],
"nist": [
"AU-9"
"AU-9 a"
],
"severity": "medium",
"description": "System executables are stored in the following directories by default:\n/bin\n/sbin\n/usr/bin\n/usr/sbin\n/usr/local/bin\n/usr/local/sbin\nThese directories should not be group-writable or world-writable.\nIf any directory DIR in these directories is found to be\ngroup-writable or world-writable, correct its permission with the\nfollowing command:\n$ sudo chmod go-w DIR",
Expand Down Expand Up @@ -134157,7 +134157,7 @@
]
}
],
"sha256": "6f23ab005bd93c425924e03d1e13dac56d502a8665cb906794e9dc3ad9baa8e4"
"sha256": "6c0f98b81de8b9feb3e92a53d18334334e8419a84aa18c5c8dba502ba2d373c6"
}
],
"passthrough": {
Expand Down
12 changes: 4 additions & 8 deletions libs/hdf-converters/src/asff-mapper/asff-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import {ExecJSON} from 'inspecjs';
import * as _ from 'lodash';
import {version as HeimdallToolsVersion} from '../../package.json';
import {BaseConverter, ILookupPath, MappedTransform} from '../base-converter';
import {
DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS,
getCCIsForNISTTags
} from '../utils/global';
import {getCMSInSpec} from './case-cms-inspec';
import {getFirewallManager} from './case-firewall-manager';
import {getGuardDuty} from './case-guardduty';
Expand All @@ -19,6 +15,8 @@ import {getPreviouslyHDF} from './case-previously-hdf';
import {getProwler} from './case-prowler';
import {getSecurityHub} from './case-security-hub';
import {getTrivy} from './case-trivy';
import {NIST2CCI} from '../mappings/CciNistMapping';
import {DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS} from '../mappings/CciNistMappingData';

const IMPACT_MAPPING: Map<string, number> = new Map([
['CRITICAL', 0.9],
Expand Down Expand Up @@ -457,11 +455,9 @@ export class ASFFMapper extends BaseConverter {
[]
) as string[];
if (tags.length === 0) {
return getCCIsForNISTTags(
DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS
);
return NIST2CCI(DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS);
} else {
return getCCIsForNISTTags(tags);
return NIST2CCI(tags);
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion libs/hdf-converters/src/asff-mapper/case-previously-hdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import * as _ from 'lodash';
import {ILookupPath, MappedTransform} from '../base-converter';
import {
conditionallyProvideAttribute,
DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS,
FROM_ASFF_TYPES_SLASH_REPLACEMENT
} from '../utils/global';
import {ASFFMapper, consolidate, SpecialCasing} from './asff-mapper';
import {DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS} from '../mappings/CciNistMappingData';

function replaceTypesSlashes<T>(type: T): T | string {
if (!_.isString(type)) {
Expand Down
Loading
Loading