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

Update logic to use XCCDF stub metadata #3046

Open
wants to merge 3 commits into
base: main
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1569,18 +1569,18 @@ generate inspec_metadata Generate an InSpec metadata template for "saf conv

#### Inspec Profile
```
generate inspec_profile Generate a new skeleton profile based on a XCCDF benchmark file
generate inspec_profile Generate a new skeleton profile based on a (STIG or CIS) XCCDF benchmark file

USAGE
$ saf generate inspec_profile -i <stig-xccdf-xml> [-o <output-folder>] [-h] [-m <metadata-json>] [-T (rule|group|cis|version)] [-s] [-L (info|warn|debug|verbose)]
$ saf generate inspec_profile -X <[stig or cis]-xccdf-xml> [-O <oval-xccdf-xml>] [-o <output-folder>] [-m <metadata-json>] [-s] [-T rule|group|cis|version] [--interactive] [-L info|warn|debug|verbose]

FLAGS
-X, --xccdfXmlFile=<value> (required) Path to the XCCDF benchmark file
-O, --ovalDefinitions=<value> Path to an OVAL definitions file to populate profile elements that reference OVAL definitions
-T, --idType=<option> [default: rule] Control ID Types: 'rule' - Vulnerability IDs (ex. 'SV-XXXXX'), 'group' -
Group IDs (ex. 'V-XXXXX'), 'cis' - CIS Rule IDs (ex.
C-1.1.1.1), 'version' - Version IDs (ex. RHEL-07-010020 - also known as STIG IDs)
<options: rule|group|cis|version>
-i, --xccdfXmlFile=<value> (required) Path to the XCCDF benchmark file
-m, --metadata=<value> Path to a JSON file with additional metadata for the inspec.yml
The metadata Json is of the following format:
{"maintainer": string, "copyright": string, "copyright_email": string, "license": string, "version": string}
Expand All @@ -1597,8 +1597,8 @@ ALIASES
$ saf generate xccdf_benchmark2inspec_stub

EXAMPLES
$ saf generate xccdf_benchmark2inspec_stub -i ./U_RHEL_6_STIG_V2R2_Manual-xccdf.xml -T group --logLevel debug -r rhel-6-update-report.md
$ saf generate xccdf_benchmark2inspec_stub -i ./CIS_Ubuntu_Linux_18.04_LTS_Benchmark_v1.1.0-xccdf.xml -O ./CIS_Ubuntu_Linux_18.04_LTS_Benchmark_v1.1.0-oval.xml --logLevel debug
$ saf generate xccdf_benchmark2inspec_stub -X ./U_RHEL_6_STIG_V2R2_Manual-xccdf.xml -T group --logLevel debug -r rhel-6-update-report.md
$ saf generate xccdf_benchmark2inspec_stub -X ./CIS_Ubuntu_Linux_18.04_LTS_Benchmark_v1.1.0-xccdf.xml -O ./CIS_Ubuntu_Linux_18.04_LTS_Benchmark_v1.1.0-oval.xml --logLevel debug
```
[top](#generate-data-reports-and-more)
#### Thresholds
Expand Down
13 changes: 0 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@
"dev": "rm -rf lib && tsc && node bin/run",
"test": "npm run test:mocha && npm run test:jest",
"tests": "npm run test",
"test:mocha": "ts-mocha --timeout 8500 --forbid-only \"test/**/*.test.ts\"",
"test:mocha:one": "ts-mocha --timeout 8500 --forbid-only",
"test:mocha": "ts-mocha --timeout 25000 --forbid-only \"test/**/*.test.ts\"",
"test:mocha:one": "ts-mocha --timeout 25000 --forbid-only",
"test:jest": "jest",
"test:jest:one": "jest --findRelatedTests",
"prepack": "run-script-os",
Expand Down
310 changes: 219 additions & 91 deletions src/commands/generate/inspec_profile.ts

Large diffs are not rendered by default.

32 changes: 20 additions & 12 deletions src/commands/generate/update_controls4delta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {Flags} from '@oclif/core'
import {createWinstonLogger} from '../../utils/logging'
import Profile from '@mitre/inspec-objects/lib/objects/profile'
import {
getExistingDescribeFromControl,
processInSpecProfile,
processXCCDF,
updateControl,
} from '@mitre/inspec-objects'
import colors from 'colors' // eslint-disable-line no-restricted-imports
import {BaseCommand} from '../../utils/oclif/baseCommand'
Expand Down Expand Up @@ -100,9 +100,9 @@ export default class GenerateUpdateControls extends BaseCommand<typeof GenerateU
const {flags} = await this.parse(GenerateUpdateControls)
const logger = createWinstonLogger('generate:update_controls', flags.logLevel)

logger.warn(colors.yellow('╔═══════════════════════════════════════════════════════════════════════════════════════════════╗'))
logger.warn(colors.yellow('║ Ensure profile controls are in cookstyle format (https://docs.chef.io/workstation/cookstyle/) ║'))
logger.warn(colors.yellow('╚═══════════════════════════════════════════════════════════════════════════════════════════════╝'))
logger.warn(colors.yellow('╔═══════════════════════════════════════════════╗'))
logger.warn(colors.yellow('║ Profile controls are formatted using Rubocop ║'))
logger.warn(colors.yellow('╚═══════════════════════════════════════════════╝'))

let inspecProfile: Profile = new Profile()
GenerateUpdateControls.backupDir = path.join(path.dirname(flags.controlsDir), 'oldControls')
Expand Down Expand Up @@ -302,14 +302,22 @@ export default class GenerateUpdateControls extends BaseCommand<typeof GenerateU
}
}

const oldControlId = control.id
const newControl = updateControl(control, xccdfNewControlsMetaDataMap.get(control.id), logger)
if (flags.formatControls) {
logger.debug(' Formatted the same way `generate delta` will write controls.')
baselineYControls.set(oldControlId, newControl.toRuby(false))
} else {
logger.debug(' Did not formatted the same way `generate delta` will write controls.')
baselineYControls.set(oldControlId, newControl.toString())
// NOTE: Not using the ts-object updateControl function as it maps the
// metadata (aka tags) from the source onto the new control. Need to add
// a new function that simply retrieves the describe block (code) from
// the source and adds to the destination (update) control)
if (xccdfNewControlsMetaDataMap.has(control.id)) {
const newControl = xccdfNewControlsMetaDataMap.get(control.id)
const existingDescribeBlock = getExistingDescribeFromControl(control)
newControl.describe = existingDescribeBlock

if (flags.formatControls) {
logger.debug(' Formatted the same way `generate delta` will write controls.')
baselineYControls.set(control.id, newControl.toRuby(false))
} else {
logger.debug(' Did not formatted the same way `generate delta` will write controls.')
baselineYControls.set(control.id, newControl.toString())
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions src/types/inspec.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,16 @@ export interface InSpecMetaData {
}

export interface InspecReadme {
profileType: string,
profileGuidance: string,
profileGuidanceAgency: string,
profileDeveloperPartner: string,
profileCompliance: string,
profileDevelopers: string,
profileName: string;
profileShortName: string;
profileTitle: string;
profileVersion: string;
stigVersion: string;
stigDate: string;
benchmarkVersion: string;
benchmarkDate: string;
}
52 changes: 26 additions & 26 deletions test/commands/attest/apply.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {expect, test} from '@oclif/test'
/* eslint-disable array-bracket-newline */
/* eslint-disable array-element-newline */
import {expect} from 'chai'
import {runCommand} from '@oclif/test'
import tmp from 'tmp'
import path from 'path'
import fs from 'fs'
Expand All @@ -8,34 +11,31 @@ describe('Test attest apply', () => {
const tmpobj = tmp.dirSync({unsafeCleanup: true})

// NOTE: replacing all CR from the files being generated to ensure proper comparison.
test
.stdout()
.stderr()
.command(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_jsonFormat.json'), '-o', `${tmpobj.name}/rhel8_attestations_jsonOutput.json`])
.it('Successfully applies a JSON attestations file', () => {
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_jsonOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))
it('Successfully applies a JSON attestations file', async () => {
await runCommand<{name: string}>(['attest apply',
'-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'),
path.resolve('./test/sample_data/attestations/attestations_jsonFormat.json'),
'-o', `${tmpobj.name}/rhel8_attestations_jsonOutput.json`,
])
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_jsonOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))

expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})
expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})

test
.stdout()
.command(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_xlsxFormat.xlsx'), '-o', `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`])
.it('Successfully applies an XLSX attestations file', () => {
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))
it('Successfully applies an XLSX attestations file', async () => {
await runCommand<{name: string}>(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_xlsxFormat.xlsx'), '-o', `${tmpobj.name}/rhel8_attestations_xlsxOutput.json`])
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_xlsxOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))

expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})
expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})

test
.stdout()
.command(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_yamlFormat.yaml'), '-o', `${tmpobj.name}/rhel8_attestations_yamlOutput.json`])
.it('Successfully applies a YAML attestations file', () => {
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_yamlOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))
it('Successfully applies a YAML attestations file', async () => {
await runCommand<{name: string}>(['attest apply', '-i', path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus.json'), path.resolve('./test/sample_data/attestations/attestations_yamlFormat.yaml'), '-o', `${tmpobj.name}/rhel8_attestations_yamlOutput.json`])
const output = JSON.parse(fs.readFileSync(`${tmpobj.name}/rhel8_attestations_yamlOutput.json`, 'utf8').replaceAll(/\r/gi, ''))
const expected = JSON.parse(fs.readFileSync(path.resolve('./test/sample_data/attestations/rhel8_sample_oneOfEachControlStatus_output.json'), 'utf8').replaceAll(/\r/gi, ''))

expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})
expect(omitHDFChangingFields(output)).to.eql(omitHDFChangingFields(expected))
})
})
Loading
Loading