diff --git a/package-lock.json b/package-lock.json index dd3a2d1f..cc05c6d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mitre/inspec-objects", - "version": "0.0.29", + "version": "0.0.32", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@mitre/inspec-objects", - "version": "0.0.29", + "version": "0.0.32", "license": "Apache-2.0", "dependencies": { "@types/flat": "^5.0.2", @@ -3872,9 +3872,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "bin": { "json5": "lib/cli.js" }, @@ -8105,9 +8105,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, "jstoxml": { "version": "3.2.3", diff --git a/src/utilities/update.ts b/src/utilities/update.ts index b5ce8abc..2e61703c 100644 --- a/src/utilities/update.ts +++ b/src/utilities/update.ts @@ -39,23 +39,150 @@ function projectValuesOntoExistingObj(dst: Record, src: Record< return dst } -/* - Return first index found from given array that is not an empty entry (cell) -*/ -function getIndexOfFirstLine(auditArray: string[], index: number, action: string): number { - let indexVal = index; - while (auditArray[indexVal] === '') { - switch (action) { - case '-': - indexVal-- - break; - case '+': - indexVal++ - break; +function getRangesForLines(text: string): number[][] { + /* + Returns an array containing two numerical indices (i.e., start and stop + line numbers) for each string or multi-line comment, given raw text as + an input parameter. The raw text is a string containing the entirety of an + InSpec control. + + Algorithm utilizes a pair of stacks (i.e., `stack`, `rangeStack`) to keep + track of string delimiters and their associated line numbers, respectively. + + Combinations Handled: + - Single quotes (') + - Double quotes (") + - Back ticks (`) + - Mixed quotes ("`'") + - Percent strings (%; keys: q, Q, r, i, I, w, W, x; delimiters: (), {}, + [], <>, most non-alphanumeric characters); (e.g., "%q()") + - Percent literals (%; delimiters: (), {}, [], <>, most non- + alphanumeric characters); (e.g., "%()") + - Multi-line comments (e.g., =begin\nSome comment\n=end) + - Variable delimiters (i.e., paranthesis: (); array: []; hash: {}) + */ + const stringDelimiters: {[key: string]: string} = {'(': ')', '{': '}', '[': ']', '<': '>'} + const variableDelimiters: {[key: string]: string} = {'(': ')', '{': '}', '[': ']'} + const quotes = '\'"`' + const strings = 'qQriIwWxs' + enum skipCharLength { + string = '('.length, + percentString = 'q('.length, + commentBegin = '=begin'.length + } + + const stack: string[] = [] + const rangeStack: number[][] = [] + const ranges: number[][] = [] + + const lines = text.split('\n') + for (let i = 0; i < lines.length; i++) { + let j = 0 + while (j < lines[i].length) { + const line = lines[i] + let char = line[j] + + const isEmptyStack = (stack.length == 0) + const isNotEmptyStack = (stack.length > 0) + + const isQuoteChar = quotes.includes(char) + const isNotEscapeChar = ((j == 0) || (j > 0 && line[j - 1] != '\\')) + const isPercentChar = (char == '%') + const isVariableDelimiterChar = Object.keys(variableDelimiters).includes(char) + const isStringDelimiterChar = ((j < line.length - 1) && (/^[^A-Za-z0-9]$/.test(line[j + 1]))) + const isCommentBeginChar = ((j == 0) && (line.length >= 6) && (line.slice(0, 6) == '=begin')) + + const isPercentStringKeyChar = ((j < line.length - 1) && (strings.includes(line[j + 1]))) + const isPercentStringDelimiterChar = ((j < line.length - 2) && (/^[^A-Za-z0-9]$/.test(line[j + 2]))) + const isPercentString = (isPercentStringKeyChar && isPercentStringDelimiterChar) + + let baseCondition = (isEmptyStack && isNotEscapeChar) + const quotePushCondition = (baseCondition && isQuoteChar) + const variablePushCondition = (baseCondition && isVariableDelimiterChar) + const stringPushCondition = (baseCondition && isPercentChar && isStringDelimiterChar) + const percentStringPushCondition = (baseCondition && isPercentChar && isPercentString) + const commentBeginCondition = (baseCondition && isCommentBeginChar) + + if (stringPushCondition) { + j += skipCharLength.string // j += 1 + } else if (percentStringPushCondition) { + j += skipCharLength.percentString // j += 2 + } else if (commentBeginCondition) { + j += skipCharLength.commentBegin // j += 6 + } + char = line[j] + + baseCondition = (isNotEmptyStack && isNotEscapeChar) + const delimiterCondition = (baseCondition && Object.keys(stringDelimiters).includes(stack[stack.length - 1])) + const delimiterPushCondition = (delimiterCondition && (stack[stack.length - 1] == char)) + const delimiterPopCondition = (delimiterCondition && (stringDelimiters[stack[stack.length - 1] as string] == char)) + const basePopCondition = (baseCondition && (stack[stack.length - 1] == char) && !Object.keys(stringDelimiters).includes(char)) + const isCommentEndChar = ((j == 0) && (line.length >= 4) && (line.slice(0, 4) == '=end')) + const commentEndCondition = (baseCondition && isCommentEndChar && (stack[stack.length - 1] == '=begin')) + + const popCondition = (basePopCondition || delimiterPopCondition || commentEndCondition) + const pushCondition = (quotePushCondition || variablePushCondition || stringPushCondition || + percentStringPushCondition || delimiterPushCondition || commentBeginCondition) + + if (popCondition) { + stack.pop() + rangeStack[rangeStack.length -1].push(i) + const range_ = rangeStack.pop() as number[] + if (rangeStack.length == 0) { + ranges.push(range_) + } + } else if (pushCondition) { + if (commentBeginCondition) { + stack.push('=begin') + } else { + stack.push(char) + } + rangeStack.push([i]) + } + j++ } } + return ranges +} - return indexVal +function joinMultiLineStringsFromRanges(text: string, ranges: number[][]): string[] { + /* + Returns an array of strings and joined strings at specified ranges, given + raw text as an input parameter. + */ + const originalLines = text.split('\n') + const joinedLines: string[] = [] + let i = 0 + while (i < originalLines.length) { + let foundInRanges = false + for (const [startIndex, stopIndex] of ranges) { + if (i >= startIndex && i <= stopIndex) { + joinedLines.push(originalLines.slice(startIndex, stopIndex + 1).join('\n')) + foundInRanges = true + i = stopIndex + break + } + } + if (!foundInRanges) { + joinedLines.push(originalLines[i]) + } + i++ + } + return joinedLines +} + +function getMultiLineRanges(ranges: number[][]): number[][] { + /* + Drops ranges with the same start and stop line numbers (i.e., strings + that populate a single line) + */ + const multiLineRanges: number[][] = [] + for (const [start, stop] of ranges) { + if (start !== stop) { + multiLineRanges.push([start, stop]) + } + } + return multiLineRanges } /* @@ -63,45 +190,32 @@ function getIndexOfFirstLine(auditArray: string[], index: number, action: string Extract the existing describe blocks (what is actually run by inspec for validation) */ export function getExistingDescribeFromControl(control: Control): string { - // Algorithm: - // Locate the start and end of the control string - // Update the end of the control that contains information (if empty lines are at the end of the control) - // loop: until the start index is changed (loop is done from the bottom up) - // Clean testing array entry line (removes any non-print characters) - // if: line starts with meta-information 'tag' or 'ref' - // set start index to found location - // break out of the loop - // end - // end - // Remove any empty lines after the start index (in any) - // Extract the describe block from the audit control given the start and end indices - // Assumptions: - // 1 - The meta-information 'tag' or 'ref' precedes the describe block - // Pros: Solves the potential issue with option 1, as the lookup for the meta-information - // 'tag' or 'ref' is expected to the at the beginning of the line. if (control.code) { - let existingDescribeBlock = '' - let indexStart = control.code.toLowerCase().indexOf('control') - let indexEnd = control.code.toLowerCase().trimEnd().lastIndexOf('end') - const auditControl = control.code.substring(indexStart, indexEnd).split('\n') - - indexStart = 0 - indexEnd = auditControl.length - 1 - indexEnd = getIndexOfFirstLine(auditControl, indexEnd, '-') - let index = indexEnd - - while (indexStart === 0) { - const line = auditControl[index].toLowerCase().trim() - if (line.indexOf('ref') === 0 || line.indexOf('tag') === 0) { - indexStart = index + 1 - } - index-- - } + // Join multi-line strings in InSpec control. + const ranges = getRangesForLines(control.code) + const multiLineRanges = getMultiLineRanges(ranges) + const lines = joinMultiLineStringsFromRanges(control.code, multiLineRanges) // Array of lines representing the full InSpec control, with multi-line strings collapsed - indexStart = getIndexOfFirstLine(auditControl, indexStart, '+') - existingDescribeBlock = auditControl.slice(indexStart, indexEnd + 1).join('\n').toString() + // Define RegExp for lines to skip when searching for describe block. + const skip = ['control\\W', ' title\\W', ' desc\\W', ' impact\\W', ' tag\\W', ' ref\\W'] + const skipRegExp = RegExp(skip.map(x => `(^${x})`).join('|')) - return existingDescribeBlock + // Extract describe block from InSpec control with collapsed multiline strings. + const describeBlock: string[] = [] + let ignoreNewLine = true + for (const line of lines) { + const checkRegExp = ((line.trim() !== '') && !skipRegExp.test(line)) + const checkNewLine = ((line.trim() === '') && !ignoreNewLine) + + // Include '\n' if it is part of describe block, otherwise skip line. + if (checkRegExp || checkNewLine) { + describeBlock.push(line) + ignoreNewLine = false + } else { + ignoreNewLine = true + } + } + return describeBlock.slice(0, describeBlock.length - 2).join('\n') // Drop trailing ['end', '\n'] from Control block. } else { return '' } @@ -199,4 +313,4 @@ export function updateProfileUsingXCCDF(from: Profile, using: string, id: 'group diff: updatedProfile.diff, markdown: markdown } -} \ No newline at end of file +} diff --git a/test/sample_data/controls-cookstyle/SV-230385.rb b/test/sample_data/controls-cookstyle/SV-230385.rb new file mode 100644 index 00000000..e13f2e41 --- /dev/null +++ b/test/sample_data/controls-cookstyle/SV-230385.rb @@ -0,0 +1,68 @@ +control 'SV-230385' do + title 'RHEL 8 must define default permissions for logon and non-logon shells.' + desc "The umask controls the default access mode assigned to newly created +files. A umask of 077 limits new files to mode 600 or less permissive. Although +umask can be represented as a four-digit number, the first digit representing +special access modes is typically ignored or required to be \"0\". This +requirement applies to the globally configured system defaults and the local +interactive user defaults for each account on the system." + desc 'rationale', '' + desc 'check', " + Verify that the umask default for installed shells is \"077\". + + Check for the value of the \"UMASK\" parameter in the \"/etc/bashrc\" and +\"/etc/csh.cshrc\" files with the following command: + + Note: If the value of the \"UMASK\" parameter is set to \"000\" in either +the \"/etc/bashrc\" or the \"/etc/csh.cshrc\" files, the Severity is raised to +a CAT I. + + # grep -i umask /etc/bashrc /etc/csh.cshrc + + /etc/bashrc: umask 077 + /etc/bashrc: umask 077 + /etc/csh.cshrc: umask 077 + /etc/csh.cshrc: umask 077 + + If the value for the \"UMASK\" parameter is not \"077\", or the \"UMASK\" +parameter is missing or is commented out, this is a finding. + " + desc 'fix', " + Configure the operating system to define default permissions for all +authenticated users in such a way that the user can only read and modify their +own files. + + Add or edit the lines for the \"UMASK\" parameter in the \"/etc/bashrc\" +and \"etc/csh.cshrc\" files to \"077\": + + UMASK 077 + " + impact 0.5 + tag severity: 'medium' + tag gtitle: 'SRG-OS-000480-GPOS-00227' + tag gid: 'V-230385' + tag rid: 'SV-230385r627750_rule' + tag stig_id: 'RHEL-08-020353' + tag fix_id: 'F-33029r567902_fix' + tag cci: ['CCI-000366'] + tag nist: ['CM-6 b'] + + umask_regexp = /umask\s*(?\d\d\d)/ + + bashrc_umask = file('/etc/bashrc').content.match(umask_regexp)[:umask_code] + cshrc_umask = file('/etc/csh.cshrc').content.match(umask_regexp)[:umask_code] + + if bashrc_umask == '000' || cshrc_umask == '000' + impact 0.7 + tag severity: 'high' + end + + describe 'umask value defined in /etc/bashrc' do + subject { bashrc_umask } + it { should cmp '077' } + end + describe 'umask value defined in /etc/csh.cshrc' do + subject { cshrc_umask } + it { should cmp '077' } + end +end diff --git a/test/sample_data/controls-for-describe-tests/array-in-header.rb b/test/sample_data/controls-for-describe-tests/array-in-header.rb new file mode 100644 index 00000000..64bc1ae6 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/array-in-header.rb @@ -0,0 +1,12 @@ +control 'array-in-header' do + tag array: [1, 2, 3, 4, 5] + tag array: [1, 2, + 3, 4, 5] + tag array: [1, + 2, + + 3, + 4, 5] + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/back-ticks.rb b/test/sample_data/controls-for-describe-tests/back-ticks.rb new file mode 100644 index 00000000..d9543049 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/back-ticks.rb @@ -0,0 +1,15 @@ +control `back-ticks` do + title `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.` + desc `Enim lobortis scelerisque \`fermentum\` dui faucibus.` + desc `\`Amet dictum sit amet justo. Massa id neque aliquam vestibulum + morbi blandit cursus risus. Rutrum tellus pellentesque eu tincidunt + tortor aliquam nulla facilisi. Molestie nunc non blandit massa enim. + At urna condimentum mattis pellentesque id nibh tortor. Amet luctus + venenatis lectus magna fringilla.\`` + impact 0.5 + tag `back-ticks`: `back ticks` + tag `escape` : `\`ticks\`` + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/comments.rb b/test/sample_data/controls-for-describe-tests/comments.rb new file mode 100644 index 00000000..6511af37 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/comments.rb @@ -0,0 +1,42 @@ +control 'comments' do + desc 'Amet volutpat consequat mauris nunc congue nisi. Quis lectus nulla + at volutpat diam ut venenatis. Venenatis a condimentum vitae sapiens.' + # Commodo sed egestas egestas fringilla. + desc 'In fermentum et sollicitudin ac orci phasellus egestas tellus. Urna + duis convallis convallis tellus id. Eget arcu dictum varius duis.' + desc 'Mauris pharetra et ultrices neque ornare. Arcu cursus vitae congue + mauris. Magna fermentum iaculis eu non diam phasellus. Sagittis purus sit + amet volutpat consequat mauris.' + # Ultricies tristique nulla aliquet enim. + describe_block = nil + # Volutpat consequat mauris nunc congue nisi. + desc 'Nunc congue nisi vitae suscipit tellus. Sagittis orci a scelerisque + purus semper eget duis at. Lacinia quis vel eros donec ac odio. ' + desc 'Dictum at tempor commodo ullamcorper a lacus vestibulum. Et magnis + dis parturient montes nascetur ridiculus mus mauris.' + +=begin +Pellentesque sit amet porttitor eget. Duis at tellus at urna. Pretium aenean +pharetra magna ac placerat vestibulum lectus mauris ultrices. Bibendum at +varius vel pharetra vel turpis nunc eget lorem. Ultrices mi tempus imperdiet +nulla malesuada pellentesque elit eget gravida. +=end + + desc 'Eget duis at tellus at urna. In hendrerit gravida rutrum quisque + non tellus orci ac auctor. Dictum at tempor commodo ullamcorper a.' + desc 'Sollicitudin aliquam ultrices sagittis orci a scelerisque. Viverra + maecenas accumsan lacus vel facilisis volutpat est velit egestas.' + + # This comment is in the describe block. + +=begin +This is a multi-line comment in the describe block. + +Vestibulum lorem sed risus ultricies tristique nulla. Interdum velit euismod +in pellentesque massa. Et magnis dis parturient montes nascetur ridiculus mus +mauris vitae. Augue lacus viverra vitae congue eu. Et ultrices neque ornare +aenean. Lectus urna duis convallis convallis tellus id interdum velit. +=end + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/double-quotes.rb b/test/sample_data/controls-for-describe-tests/double-quotes.rb new file mode 100644 index 00000000..088f1122 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/double-quotes.rb @@ -0,0 +1,15 @@ +control "double-quotes" do + title "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua." + desc "Enim lobortis scelerisque \"fermentum\" dui faucibus." + desc "\"Amet dictum sit amet justo. Massa id neque aliquam vestibulum + morbi blandit cursus risus. Rutrum tellus pellentesque eu tincidunt + tortor aliquam nulla facilisi. Molestie nunc non blandit massa enim. + At urna condimentum mattis pellentesque id nibh tortor. Amet luctus + venenatis lectus magna fringilla.\"" + impact 0.5 + tag "quotes": "double quotes" + tag "escape": "\"double quotes\"" + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-of-line.rb b/test/sample_data/controls-for-describe-tests/end-of-line.rb new file mode 100644 index 00000000..276443d3 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-of-line.rb @@ -0,0 +1,22 @@ +control 'end-of-line' do + title ' + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do +eiusmod tempor incididunt ut labore et dolore magna aliqua. +' + desc %( +Nisi scelerisque eu ultrices vitae auctor eu augue. + Nullam vehicula ipsum a arcu cursus vitae congue mauris. + ) + desc %q[ + Nunc sed id semper risus. Scelerisque fermentum dui faucibus in +ornare quam viverra. Faucibus in ornare quam viverra orci sagittis + eu volutpat odio. + ] + desc %w. + Amet est placerat in egestas erat imperdiet sed\. Duis at consectetur +lorem donec massa sapien\. Amet dictum sit amet justo donec enim diam + vulputate ut\. + . + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-control.rb b/test/sample_data/controls-for-describe-tests/end-on-control.rb new file mode 100644 index 00000000..f9972857 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-control.rb @@ -0,0 +1,3 @@ +control 'end-on-control' do + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-desc.rb b/test/sample_data/controls-for-describe-tests/end-on-desc.rb new file mode 100644 index 00000000..142147c9 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-desc.rb @@ -0,0 +1,10 @@ +control 'end-on-desc' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + impact 0.5 + tag 'End on': 'desc' + ref 'https://example.com' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-impact.rb b/test/sample_data/controls-for-describe-tests/end-on-impact.rb new file mode 100644 index 00000000..53a588e8 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-impact.rb @@ -0,0 +1,10 @@ +control 'end-on-impact' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + tag 'End on': 'impact' + ref 'https://example.com' + impact 0.5 + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-ref.rb b/test/sample_data/controls-for-describe-tests/end-on-ref.rb new file mode 100644 index 00000000..a5ed7248 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-ref.rb @@ -0,0 +1,10 @@ +control 'end-on-ref' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + impact 0.5 + tag 'End on': 'impact' + ref 'https://example.com' + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-tag.rb b/test/sample_data/controls-for-describe-tests/end-on-tag.rb new file mode 100644 index 00000000..abe7c547 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-tag.rb @@ -0,0 +1,10 @@ +control 'end-on-tag' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + ref 'https://example.com' + impact 0.5 + tag 'End on': 'tag' + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/end-on-title.rb b/test/sample_data/controls-for-describe-tests/end-on-title.rb new file mode 100644 index 00000000..0112468a --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/end-on-title.rb @@ -0,0 +1,6 @@ +control 'end-on-title' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/hash-in-header.rb b/test/sample_data/controls-for-describe-tests/hash-in-header.rb new file mode 100644 index 00000000..71e3384a --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/hash-in-header.rb @@ -0,0 +1,13 @@ +control 'hash-in-header' do + tag hash: { 'one' => '1', 'two' => '2', 'three' => '3' } + tag hash: { 'one' => '1', + 'two' => '2', + 'three' => '3' } + tag hash: { 'one' => '1', +'two' => '2', + + 'three' => '3', +} + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/headers-in-describe.rb b/test/sample_data/controls-for-describe-tests/headers-in-describe.rb new file mode 100644 index 00000000..17d7b362 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/headers-in-describe.rb @@ -0,0 +1,16 @@ +control 'headers-in-describe' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + impact 0.5 + tag 'End on': 'desc' + ref 'https://example.com' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + + describe_block = nil + if describe_block + impact 1.0 + tag 'headers': 'in describe' + ref 'https://sample.com' + desc 'Amet dictum sit amet justo.' + end +end diff --git a/test/sample_data/controls-for-describe-tests/keywords-in-strings.rb b/test/sample_data/controls-for-describe-tests/keywords-in-strings.rb new file mode 100644 index 00000000..a26696cd --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/keywords-in-strings.rb @@ -0,0 +1,12 @@ +control 'keywords-in-strings' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + impact 0.5 + tag 'End on': 'desc' + ref 'https://example.com' + desc 'Enim lobortis scelerisque fermentum dui faucibus.' + + keyword_in_string = 'Lorem ipsum desc test control' + describe_block = nil +end + \ No newline at end of file diff --git a/test/sample_data/controls-for-describe-tests/mixed-quotes.rb b/test/sample_data/controls-for-describe-tests/mixed-quotes.rb new file mode 100644 index 00000000..758d8883 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/mixed-quotes.rb @@ -0,0 +1,18 @@ +control "'`mixed-quotes`'" do + title '"`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.`"' + desc "'`Enim \`lobortis\` scelerisque \"fermentum\" dui \'faucibus\'.`'" + desc `\'Amet dictum sit amet justo. Massa id neque aliquam vestibulum + morbi blandit cursus risus.\' \"Rutrum tellus pellentesque eu tincidunt + tortor aliquam nulla facilisi. Molestie' nunc non blandit massa enim. + At urna condimentum mattis pellentesque id nibh tortor.\" Amet luctus" + venenatis lectus magna fringilla.` + impact 0.5 + tag 'quote': "mixed quotes" + tag "escape": '"\'mixed \"quotes\""\'' + tag 'uncaught apostrophe': "What's this?" + tag "uncaught double quote": 'What does `"` do?' + tag `uncaught backtick`: "What's this '`' mean?" + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/multi-line-describe-block.rb b/test/sample_data/controls-for-describe-tests/multi-line-describe-block.rb new file mode 100644 index 00000000..97407384 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/multi-line-describe-block.rb @@ -0,0 +1,7 @@ +control 'multi-line-describe-block' do + describe 'Sed enim ut sem viverra. Elit pellentesque habitant morbi + tristique senectus et netus et malesuada. At tempor commodo ullamcorper + a lacus vestibulum.' do + describe_block = true + end +end diff --git a/test/sample_data/controls-for-describe-tests/paranthesis-in-header.rb b/test/sample_data/controls-for-describe-tests/paranthesis-in-header.rb new file mode 100644 index 00000000..89c92414 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/paranthesis-in-header.rb @@ -0,0 +1,10 @@ +control 'paranthesis-in-header' do + impact(0.5) + impact(0.5 + ) + impact( + 0.5 +) + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/percent-literals.rb b/test/sample_data/controls-for-describe-tests/percent-literals.rb new file mode 100644 index 00000000..ffa728d8 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/percent-literals.rb @@ -0,0 +1,62 @@ +control 'percent-literals' do + desc %(Lorem ipsum dolor sit amet, (consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore) et dolore magna aliqua.) + desc %[Enim lobortis scelerisque fermentum dui faucibus. [Amet dictum sit + amet justo.] Massa id neque aliquam vestibulum morbi blandit cursus.] + desc %{Rutrum tellus pellentesque eu tincidunt {tortor aliquam nulla + facilisi.} Molestie nunc non blandit massa enim.} + desc % lectus magna fringilla.> + desc %!Massa tincidunt nunc pulvinar sapien et. Blandit libero volutpat + sed cras. Sollicitudin aliquam ultrices sagittis orci.! + desc %`Duis convallis convallis tellus id interdum. Quis viverra nibh cras + pulvinar mattis nunc sed.` + desc %~At ultrices mi tempus imperdiet nulla malesuada pellentesque elit. + Commodo odio aenean sed adipiscing.~ + desc %!Non tellus orci ac auctor augue mauris. Sit amet justo donec enim + diam vulputate ut. Elementum nisi quis eleifend quam adipiscing vitae + proin sagittis nisl.! + desc %@Penatibus et magnis dis parturient montes nascetur ridiculus mus + mauris. Tincidunt eget nullam non nisi est.@ + desc %#Dictumst vestibulum rhoncus est pellentesque. Proin sed libero + enim sed faucibus turpis in. Mattis enim ut tellus elementum sagittis.# + desc %$Vestibulum sed arcu non odio euismod. Volutpat blandit aliquam + etiam erat velit scelerisque.$ + desc %%Quis vel eros donec ac odio tempor orci dapibus. Faucibus interdum + posuere lorem ipsum.% + desc %^Faucibus ornare suspendisse sed nisi lacus. Aliquet bibendum enim + facilisis gravida. Ut sem nulla pharetra diam sit amet nisl.^ + desc %&Orci dapibus ultrices in iaculis nunc sed augue lacus viverra. + Interdum velit laoreet id donec ultrices.& + desc %*Augue neque gravida in fermentum et sollicitudin ac orci phasellus. + Suspendisse in est ante in nibh.* + desc %-Proin nibh nisl condimentum id venenatis a. Consequat ac felis + donec et odio pellentesque diam.- + desc %_Magna eget est lorem ipsum dolor sit amet. Condimentum lacinia + quis vel eros. Risus at ultrices mi tempus imperdiet._ + desc %\Dignissim sodales ut eu sem integer vitae justo eget. Proin + sagittis nisl rhoncus mattis rhoncus urna neque.\ + desc %|Pulvinar pellentesque habitant morbi tristique senectus et netus + et malesuada. Massa tincidunt dui ut ornare lectus sit amet est.| + desc %;Malesuada bibendum arcu vitae elementum curabitur vitae. Blandit + aliquam etiam erat velit scelerisque in dictum non.; + desc %:Vitae purus faucibus ornare suspendisse sed nisi. Dignissim diam + quis enim lobortis.: + desc %"Massa tempor nec feugiat nisl pretium. Sit amet dictum sit amet. + Sem integer vitae justo eget magna fermentum iaculis eu." + desc %'Ut tellus elementum sagittis vitae. Turpis egestas maecenas + pharetra convallis. Ornare massa eget egestas purus viverra accumsan.' + desc %,Sed libero enim sed faucibus turpis. Risus sed vulputate odio ut + enim blandit volutpat maecenas., + desc %.Proin sed libero enim sed faucibus turpis in\. Metus dictum at + tempor commodo\. Luctus venenatis lectus magna fringilla urna porttitor + rhoncus dolor. + desc %?Morbi quis commodo odio aenean sed adipiscing diam donec. + Condimentum mattis pellentesque id nibh tortor id aliquet.? + desc %/Malesuada fames ac turpis egestas integer eget aliquet nibh + praesent. Lobortis feugiat vivamus at augue eget arcu./ + desc %+Vulputate eu scelerisque felis imperdiet. Elit at imperdiet dui + accumsan sit amet nulla.+ + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/percent-strings.rb b/test/sample_data/controls-for-describe-tests/percent-strings.rb new file mode 100644 index 00000000..59c45a06 --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/percent-strings.rb @@ -0,0 +1,23 @@ +control 'percent-strings' do + desc %q(Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.) + desc %Q[Adipiscing at in tellus integer feugiat scelerisque varius morbi + enim. Ac felis donec et odio pellentesque.] + desc %r{Molestie ac feugiat sed lectus. Feugiat in ante metus dictum at. + Arcu felis bibendum ut tristique et. Non pulvinar neque laoreet + suspendisse interdum consectetur libero.} + desc %i + desc %I(Fusce ut placerat orci nulla pellentesque dignissim enim sit + amet. Sodales ut etiam sit amet nisl.) + desc %w[Faucibus pulvinar elementum integer enim neque volutpat ac + tincidunt. Dui id ornare arcu odio.] + desc %W{Sagittis vitae et leo duis ut diam quam. Sit amet consectetur + adipiscing elit ut aliquam. Et tortor at risus viverra adipiscing at.} + desc %x + desc %s(Odio tempor orci dapibus ultrices in. Elementum sagittis vitae et + leo duis ut. Felis eget nunc lobortis mattis aliquam faucibus purus.) + + describe_block = nil +end diff --git a/test/sample_data/controls-for-describe-tests/single-quotes.rb b/test/sample_data/controls-for-describe-tests/single-quotes.rb new file mode 100644 index 00000000..38070e2b --- /dev/null +++ b/test/sample_data/controls-for-describe-tests/single-quotes.rb @@ -0,0 +1,15 @@ +control 'single-quotes' do + title 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua.' + desc 'Enim lobortis scelerisque \'fermentum\' dui faucibus.' + desc '\'Amet dictum sit amet justo. Massa id neque aliquam vestibulum + morbi blandit cursus risus. Rutrum tellus pellentesque eu tincidunt + tortor aliquam nulla facilisi. Molestie nunc non blandit massa enim. + At urna condimentum mattis pellentesque id nibh tortor. Amet luctus + venenatis lectus magna fringilla.\'' + impact 0.5 + tag 'quotes': 'single quotes' + tag 'escape' : '\'double quotes\'' + + describe_block = nil +end diff --git a/test/sample_data/inspec/json/cookstyle-controls-profile.json b/test/sample_data/inspec/json/cookstyle-controls-profile.json index b986446d..d334de23 100644 --- a/test/sample_data/inspec/json/cookstyle-controls-profile.json +++ b/test/sample_data/inspec/json/cookstyle-controls-profile.json @@ -1 +1 @@ -{"supports":[],"title":"tests from ./test/sample_data/controls-cookstyle","name":"tests from ..test.sample_data.controls-cookstyle","controls":[{"title":"The Red Hat Enterprise Linux operating system must be configured so that all local initialization files for\n interactive users are owned by the home directory user or root.","desc":"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.","descriptions":{"default":"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.","check":"Verify the local initialization files of all local interactive users are owned by that user.\n Check the home directory assignment for all non-privileged users on the system with the following command:\n Note: The example will be for the smithj user, who has a home directory of \"/home/smithj\".\n # awk -F: '($3>=1000)&&($7 !~ /nologin/){print $1, $3, $6}' /etc/passwd\n smithj 1000 /home/smithj\n Note: This may miss interactive users that have been assigned a privileged User Identifier (UID). Evidence of\n interactive use may be obtained from a number of log files containing system logon information.\n Check the owner of all local interactive user's initialization files with the following command:\n # ls -al /home/smithj/.[^.]* | more\n -rwxr-xr-x 1 smithj users 896 Mar 10 2011 .profile\n -rwxr-xr-x 1 smithj users 497 Jan 6 2007 .login\n -rwxr-xr-x 1 smithj users 886 Jan 6 2007 .something\n If all local interactive user's initialization files are not owned by that user or root, this is a finding.","fix":"Set the owner of the local initialization files for interactive users to\neither the directory owner or root with the following command:\n Note: The example will be for the smithj user, who has a home directory of\n\"/home/smithj\".\n # chown smithj /home/smithj/.[^.]*"},"impact":0.5,"refs":[],"tags":{"legacy":["V-72029","SV-86653"],"severity":"medium","gtitle":"SRG-OS-000480-GPOS-00227","gid":"V-204474","rid":"SV-204474r603834_rule","stig_id":"RHEL-07-020690","fix_i":"F-4598r462464_fix","cci":["CCI-000366"],"nist":["CM-6 b"],"subsystems":["init_files"]},"code":"control 'SV-204474' do\n title \"The Red Hat Enterprise Linux operating system must be configured so that all local initialization files for\n interactive users are owned by the home directory user or root.\"\n desc \"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.\"\n desc 'check', \"Verify the local initialization files of all local interactive users are owned by that user.\n Check the home directory assignment for all non-privileged users on the system with the following command:\n Note: The example will be for the smithj user, who has a home directory of \\\"/home/smithj\\\".\n # awk -F: '($3>=1000)&&($7 !~ /nologin/){print $1, $3, $6}' /etc/passwd\n smithj 1000 /home/smithj\n Note: This may miss interactive users that have been assigned a privileged User Identifier (UID). Evidence of\n interactive use may be obtained from a number of log files containing system logon information.\n Check the owner of all local interactive user's initialization files with the following command:\n # ls -al /home/smithj/.[^.]* | more\n -rwxr-xr-x 1 smithj users 896 Mar 10 2011 .profile\n -rwxr-xr-x 1 smithj users 497 Jan 6 2007 .login\n -rwxr-xr-x 1 smithj users 886 Jan 6 2007 .something\n If all local interactive user's initialization files are not owned by that user or root, this is a finding.\"\n desc 'fix', \"Set the owner of the local initialization files for interactive users to\neither the directory owner or root with the following command:\n Note: The example will be for the smithj user, who has a home directory of\n\\\"/home/smithj\\\".\n # chown smithj /home/smithj/.[^.]*\"\n impact 0.5\n tag legacy: ['V-72029', 'SV-86653']\n tag severity: 'medium'\n tag gtitle: 'SRG-OS-000480-GPOS-00227'\n tag gid: 'V-204474'\n tag rid: 'SV-204474r603834_rule'\n tag stig_id: 'RHEL-07-020690'\n tag fix_i: 'F-4598r462464_fix'\n tag cci: ['CCI-000366']\n tag nist: ['CM-6 b']\n tag subsystems: ['init_files']\n\n if virtualization.system.eql?('docker')\n impact 0.0\n describe 'Control not applicable to a container' do\n skip 'Control not applicable to a container'\n end\n else\n\n exempt_home_users = input('exempt_home_users')\n non_interactive_shells = input('non_interactive_shells')\n\n ignore_shells = non_interactive_shells.join('|')\n\n findings = Set[]\n users.where { !shell.match(ignore_shells) && (uid >= 1000 || uid == 0) }.entries.each do |user_info|\n next if exempt_home_users.include?(user_info.username.to_s)\n\n findings += command(\"find #{user_info.home} -name '.*' -not -user #{user_info.username} -a -not -user root\").stdout.split(\"\\n\")\n end\n describe 'Files and Directories not owned by the user or root of the parent home directory' do\n subject { findings.to_a }\n it { should be_empty }\n end\n end\nend\n","source_location":{"ref":"./test/sample_data/controls-cookstyle/SV-204474.rb","line":1},"id":"SV-204474"},{"title":"The Red Hat Enterprise Linux operating system must be configured so that the file permissions, ownership,\n and group membership of system files and commands match the vendor values.","desc":"Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.","descriptions":{"default":"Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.","rationale":"","check":"Verify the file permissions, ownership, and group membership of system files and commands match the\n vendor values.\n Check the default file permissions, ownership, and group membership of system files and commands with the following\n command:\n # for i in `rpm -Va | egrep '^.{1}M|^.{5}U|^.{6}G' | cut -d \" \" -f 4,5`;do for j in `rpm -qf $i`;do rpm -ql $j\n --dump | cut -d \" \" -f 1,5,6,7 | grep $i;done;done\n /var/log/gdm 040755 root root\n /etc/audisp/audisp-remote.conf 0100640 root root\n /usr/bin/passwd 0104755 root root\n For each file returned, verify the current permissions, ownership, and group membership:\n # ls -la \n -rw-------. 1 root root 133 Jan 11 13:25 /etc/audisp/audisp-remote.conf\n If the file is more permissive than the default permissions, this is a finding.\n If the file is not owned by the default owner and is not documented with the Information System Security Officer\n (ISSO), this is a finding.\n If the file is not a member of the default group and is not documented with the Information System Security Officer\n (ISSO), this is a finding.","fix":"Run the following command to determine which package owns the file:\n\n # rpm -qf \n\n Reset the user and group ownership of files within a package with the\nfollowing command:\n\n #rpm --setugids \n\n\n Reset the permissions of files within a package with the following command:\n\n #rpm --setperms "},"impact":0.7,"refs":[],"tags":{"legacy":["V-71849","SV-86473"],"severity":"high","gtitle":"SRG-OS-000257-GPOS-00098","satisfies":["SRG-OS-000257-GPOS-00098","SRG-OS-000278-GPOS-00108"],"gid":"V-204392","rid":"SV-204392r646841_rule","stig_id":"RHEL-07-010010","fix_id":"F-36302r646840_fix","cci":["CCI-001494","CCI-001496","CCI-002165","CCI-002235"],"nist":["AU-9","AU-9 (3)","AC-3 (4)","AC-6 (10)"],"subsystems":["permissions","package","rpm"],"host":null,"container":null},"code":"control 'SV-204392' do\n title 'The Red Hat Enterprise Linux operating system must be configured so that the file permissions, ownership,\n and group membership of system files and commands match the vendor values.'\n desc 'Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.'\n desc 'rationale', ''\n desc 'check', %\n -rw-------. 1 root root 133 Jan 11 13:25 /etc/audisp/audisp-remote.conf\n If the file is more permissive than the default permissions, this is a finding.\n If the file is not owned by the default owner and is not documented with the Information System Security Officer\n (ISSO), this is a finding.\n If the file is not a member of the default group and is not documented with the Information System Security Officer\n (ISSO), this is a finding.>\n desc 'fix', \"\n Run the following command to determine which package owns the file:\n\n # rpm -qf \n\n Reset the user and group ownership of files within a package with the\n following command:\n\n #rpm --setugids \n\n\n Reset the permissions of files within a package with the following command:\n\n #rpm --setperms \n \"\n impact 0.7\n tag 'legacy': ['V-71849', 'SV-86473']\n tag 'severity': 'high'\n tag 'gtitle': 'SRG-OS-000257-GPOS-00098'\n tag 'satisfies': ['SRG-OS-000257-GPOS-00098', 'SRG-OS-000278-GPOS-00108']\n tag 'gid': 'V-204392'\n tag 'rid': 'SV-204392r646841_rule'\n tag 'stig_id': 'RHEL-07-010010'\n tag 'fix_id': 'F-36302r646840_fix'\n tag 'cci': ['CCI-001494', 'CCI-001496', 'CCI-002165', 'CCI-002235']\n tag nist: ['AU-9', 'AU-9 (3)', 'AC-3 (4)', 'AC-6 (10)']\n tag subsystems: ['permissions', 'package', 'rpm']\n tag 'host', 'container'\n\n if input('disable_slow_controls')\n describe \"This control consistently takes a long time to run and has been disabled\n using the disable_slow_controls attribute.\" do\n skip \"This control consistently takes a long time to run and has been disabled\n using the disable_slow_controls attribute. You must enable this control for a\n full accredidation for production.\"\n end\n else\n\n allowlist = input('rpm_verify_perms_except')\n\n misconfigured_packages = command('rpm -Va').stdout.split(\"\\n\")\n .select { |package| package[0..7].match(/M|U|G/) }\n .map { |package| package.match(/\\S+$/)[0] }\n\n if misconfigured_packages.empty?\n describe 'The list of rpm packages with permissions changed from the vendor values' do\n subject { misconfigured_packages }\n it { should be_empty }\n end\n else\n describe 'The list of rpm packages with permissions changed from the vendor values' do\n fail_msg = \"Files that have been modified from vendor-approved permissions but are not in the allowlist: #{(misconfigured_packages - allowlist).join(', ')}\"\n it 'should all appear in the allowlist' do\n expect(misconfigured_packages).to all(be_in(allowlist)), fail_msg\n end\n end\n end\n end\nend\n","source_location":{"ref":"./test/sample_data/controls-cookstyle/SV-204392.rb","line":1},"id":"SV-204392"}],"groups":[{"title":null,"controls":["SV-204474"],"id":"SV-204474.rb"},{"title":null,"controls":["SV-204392"],"id":"SV-204392.rb"}],"inputs":[{"name":"exempt_home_users","options":{"value":"Input 'exempt_home_users' does not have a value. Skipping test."}},{"name":"non_interactive_shells","options":{"value":"Input 'non_interactive_shells' does not have a value. Skipping test."}},{"name":"disable_slow_controls","options":{"value":"Input 'disable_slow_controls' does not have a value. Skipping test."}}],"sha256":"317c2a47b14508be64d5a6c370d8e0f13602df559210a3d25bf5a9429b42b0f1","status_message":"","status":"loaded","generator":{"name":"inspec","version":"5.18.14"}} +{"supports":[],"title":"tests from ./test/sample_data/controls-cookstyle","name":"tests from ..test.sample_data.controls-cookstyle","controls":[{"title":"The Red Hat Enterprise Linux operating system must be configured so that all local initialization files for\n interactive users are owned by the home directory user or root.","desc":"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.","descriptions":{"default":"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.","check":"Verify the local initialization files of all local interactive users are owned by that user.\n Check the home directory assignment for all non-privileged users on the system with the following command:\n Note: The example will be for the smithj user, who has a home directory of \"/home/smithj\".\n # awk -F: '($3>=1000)&&($7 !~ /nologin/){print $1, $3, $6}' /etc/passwd\n smithj 1000 /home/smithj\n Note: This may miss interactive users that have been assigned a privileged User Identifier (UID). Evidence of\n interactive use may be obtained from a number of log files containing system logon information.\n Check the owner of all local interactive user's initialization files with the following command:\n # ls -al /home/smithj/.[^.]* | more\n -rwxr-xr-x 1 smithj users 896 Mar 10 2011 .profile\n -rwxr-xr-x 1 smithj users 497 Jan 6 2007 .login\n -rwxr-xr-x 1 smithj users 886 Jan 6 2007 .something\n If all local interactive user's initialization files are not owned by that user or root, this is a finding.","fix":"Set the owner of the local initialization files for interactive users to\neither the directory owner or root with the following command:\n Note: The example will be for the smithj user, who has a home directory of\n\"/home/smithj\".\n # chown smithj /home/smithj/.[^.]*"},"impact":0.5,"refs":[],"tags":{"legacy":["V-72029","SV-86653"],"severity":"medium","gtitle":"SRG-OS-000480-GPOS-00227","gid":"V-204474","rid":"SV-204474r603834_rule","stig_id":"RHEL-07-020690","fix_i":"F-4598r462464_fix","cci":["CCI-000366"],"nist":["CM-6 b"],"subsystems":["init_files"]},"code":"control 'SV-204474' do\n title 'The Red Hat Enterprise Linux operating system must be configured so that all local initialization files for\n interactive users are owned by the home directory user or root.'\n desc \"Local initialization files are used to configure the user's shell environment upon logon. Malicious\n modification of these files could compromise accounts upon logon.\"\n desc 'check', %q(Verify the local initialization files of all local interactive users are owned by that user.\n Check the home directory assignment for all non-privileged users on the system with the following command:\n Note: The example will be for the smithj user, who has a home directory of \"/home/smithj\".\n # awk -F: '($3>=1000)&&($7 !~ /nologin/){print $1, $3, $6}' /etc/passwd\n smithj 1000 /home/smithj\n Note: This may miss interactive users that have been assigned a privileged User Identifier (UID). Evidence of\n interactive use may be obtained from a number of log files containing system logon information.\n Check the owner of all local interactive user's initialization files with the following command:\n # ls -al /home/smithj/.[^.]* | more\n -rwxr-xr-x 1 smithj users 896 Mar 10 2011 .profile\n -rwxr-xr-x 1 smithj users 497 Jan 6 2007 .login\n -rwxr-xr-x 1 smithj users 886 Jan 6 2007 .something\n If all local interactive user's initialization files are not owned by that user or root, this is a finding.)\n desc 'fix', 'Set the owner of the local initialization files for interactive users to\neither the directory owner or root with the following command:\n Note: The example will be for the smithj user, who has a home directory of\n\"/home/smithj\".\n # chown smithj /home/smithj/.[^.]*'\n impact 0.5\n tag legacy: ['V-72029', 'SV-86653']\n tag severity: 'medium'\n tag gtitle: 'SRG-OS-000480-GPOS-00227'\n tag gid: 'V-204474'\n tag rid: 'SV-204474r603834_rule'\n tag stig_id: 'RHEL-07-020690'\n tag fix_i: 'F-4598r462464_fix'\n tag cci: ['CCI-000366']\n tag nist: ['CM-6 b']\n tag subsystems: ['init_files']\n\n if virtualization.system.eql?('docker')\n impact 0.0\n describe 'Control not applicable to a container' do\n skip 'Control not applicable to a container'\n end\n else\n\n exempt_home_users = input('exempt_home_users')\n non_interactive_shells = input('non_interactive_shells')\n\n ignore_shells = non_interactive_shells.join('|')\n\n findings = Set[]\n users.where { !shell.match(ignore_shells) && (uid >= 1000 || uid == 0) }.entries.each do |user_info|\n next if exempt_home_users.include?(user_info.username.to_s)\n\n findings += command(\"find #{user_info.home} -name '.*' -not -user #{user_info.username} -a -not -user root\").stdout.split(\"\\n\")\n end\n describe 'Files and Directories not owned by the user or root of the parent home directory' do\n subject { findings.to_a }\n it { should be_empty }\n end\n end\nend\n","source_location":{"ref":"./test/sample_data/controls-cookstyle/SV-204474.rb","line":1},"id":"SV-204474"},{"title":"RHEL 8 must define default permissions for logon and non-logon shells.","desc":"The umask controls the default access mode assigned to newly created\nfiles. A umask of 077 limits new files to mode 600 or less permissive. Although\numask can be represented as a four-digit number, the first digit representing\nspecial access modes is typically ignored or required to be \"0\". This\nrequirement applies to the globally configured system defaults and the local\ninteractive user defaults for each account on the system.","descriptions":{"default":"The umask controls the default access mode assigned to newly created\nfiles. A umask of 077 limits new files to mode 600 or less permissive. Although\numask can be represented as a four-digit number, the first digit representing\nspecial access modes is typically ignored or required to be \"0\". This\nrequirement applies to the globally configured system defaults and the local\ninteractive user defaults for each account on the system.","rationale":"","check":"Verify that the umask default for installed shells is \"077\".\n\n Check for the value of the \"UMASK\" parameter in the \"/etc/bashrc\" and\n\"/etc/csh.cshrc\" files with the following command:\n\n Note: If the value of the \"UMASK\" parameter is set to \"000\" in either\nthe \"/etc/bashrc\" or the \"/etc/csh.cshrc\" files, the Severity is raised to\na CAT I.\n\n # grep -i umask /etc/bashrc /etc/csh.cshrc\n\n /etc/bashrc: umask 077\n /etc/bashrc: umask 077\n /etc/csh.cshrc: umask 077\n /etc/csh.cshrc: umask 077\n\n If the value for the \"UMASK\" parameter is not \"077\", or the \"UMASK\"\nparameter is missing or is commented out, this is a finding.","fix":"Configure the operating system to define default permissions for all\nauthenticated users in such a way that the user can only read and modify their\nown files.\n\n Add or edit the lines for the \"UMASK\" parameter in the \"/etc/bashrc\"\nand \"etc/csh.cshrc\" files to \"077\":\n\n UMASK 077"},"impact":0.5,"refs":[],"tags":{"severity":"medium","gtitle":"SRG-OS-000480-GPOS-00227","gid":"V-230385","rid":"SV-230385r627750_rule","stig_id":"RHEL-08-020353","fix_id":"F-33029r567902_fix","cci":["CCI-000366"],"nist":["CM-6 b"]},"code":"control 'SV-230385' do\n title 'RHEL 8 must define default permissions for logon and non-logon shells.'\n desc \"The umask controls the default access mode assigned to newly created\nfiles. A umask of 077 limits new files to mode 600 or less permissive. Although\numask can be represented as a four-digit number, the first digit representing\nspecial access modes is typically ignored or required to be \\\"0\\\". This\nrequirement applies to the globally configured system defaults and the local\ninteractive user defaults for each account on the system.\"\n desc 'rationale', ''\n desc 'check', \"\n Verify that the umask default for installed shells is \\\"077\\\".\n\n Check for the value of the \\\"UMASK\\\" parameter in the \\\"/etc/bashrc\\\" and\n\\\"/etc/csh.cshrc\\\" files with the following command:\n\n Note: If the value of the \\\"UMASK\\\" parameter is set to \\\"000\\\" in either\nthe \\\"/etc/bashrc\\\" or the \\\"/etc/csh.cshrc\\\" files, the Severity is raised to\na CAT I.\n\n # grep -i umask /etc/bashrc /etc/csh.cshrc\n\n /etc/bashrc: umask 077\n /etc/bashrc: umask 077\n /etc/csh.cshrc: umask 077\n /etc/csh.cshrc: umask 077\n\n If the value for the \\\"UMASK\\\" parameter is not \\\"077\\\", or the \\\"UMASK\\\"\nparameter is missing or is commented out, this is a finding.\n \"\n desc 'fix', \"\n Configure the operating system to define default permissions for all\nauthenticated users in such a way that the user can only read and modify their\nown files.\n\n Add or edit the lines for the \\\"UMASK\\\" parameter in the \\\"/etc/bashrc\\\"\nand \\\"etc/csh.cshrc\\\" files to \\\"077\\\":\n\n UMASK 077\n \"\n impact 0.5\n tag severity: 'medium'\n tag gtitle: 'SRG-OS-000480-GPOS-00227'\n tag gid: 'V-230385'\n tag rid: 'SV-230385r627750_rule'\n tag stig_id: 'RHEL-08-020353'\n tag fix_id: 'F-33029r567902_fix'\n tag cci: ['CCI-000366']\n tag nist: ['CM-6 b']\n\n umask_regexp = /umask\\s*(?\\d\\d\\d)/\n\n bashrc_umask = file('/etc/bashrc').content.match(umask_regexp)[:umask_code]\n cshrc_umask = file('/etc/csh.cshrc').content.match(umask_regexp)[:umask_code]\n\n if bashrc_umask == '000' || cshrc_umask == '000'\n impact 0.7\n tag severity: 'high'\n end\n\n describe 'umask value defined in /etc/bashrc' do\n subject { bashrc_umask }\n it { should cmp '077' }\n end\n describe 'umask value defined in /etc/csh.cshrc' do\n subject { cshrc_umask }\n it { should cmp '077' }\n end\nend\n","source_location":{"ref":"./test/sample_data/controls-cookstyle/SV-230385.rb","line":1},"id":"SV-230385"},{"title":"The Red Hat Enterprise Linux operating system must be configured so that the file permissions, ownership,\n and group membership of system files and commands match the vendor values.","desc":"Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.","descriptions":{"default":"Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.","rationale":"","check":"Verify the file permissions, ownership, and group membership of system files and commands match the\n vendor values.\n Check the default file permissions, ownership, and group membership of system files and commands with the following\n command:\n # for i in `rpm -Va | egrep '^.{1}M|^.{5}U|^.{6}G' | cut -d \" \" -f 4,5`;do for j in `rpm -qf $i`;do rpm -ql $j\n --dump | cut -d \" \" -f 1,5,6,7 | grep $i;done;done\n /var/log/gdm 040755 root root\n /etc/audisp/audisp-remote.conf 0100640 root root\n /usr/bin/passwd 0104755 root root\n For each file returned, verify the current permissions, ownership, and group membership:\n # ls -la \n -rw-------. 1 root root 133 Jan 11 13:25 /etc/audisp/audisp-remote.conf\n If the file is more permissive than the default permissions, this is a finding.\n If the file is not owned by the default owner and is not documented with the Information System Security Officer\n (ISSO), this is a finding.\n If the file is not a member of the default group and is not documented with the Information System Security Officer\n (ISSO), this is a finding.","fix":"Run the following command to determine which package owns the file:\n\n # rpm -qf \n\n Reset the user and group ownership of files within a package with the\nfollowing command:\n\n #rpm --setugids \n\n\n Reset the permissions of files within a package with the following command:\n\n #rpm --setperms "},"impact":0.7,"refs":[],"tags":{"legacy":["V-71849","SV-86473"],"severity":"high","gtitle":"SRG-OS-000257-GPOS-00098","satisfies":["SRG-OS-000257-GPOS-00098","SRG-OS-000278-GPOS-00108"],"gid":"V-204392","rid":"SV-204392r646841_rule","stig_id":"RHEL-07-010010","fix_id":"F-36302r646840_fix","cci":["CCI-001494","CCI-001496","CCI-002165","CCI-002235"],"nist":["AU-9","AU-9 (3)","AC-3 (4)","AC-6 (10)"],"subsystems":["permissions","package","rpm"],"host":null,"container":null},"code":"control 'SV-204392' do\n title 'The Red Hat Enterprise Linux operating system must be configured so that the file permissions, ownership,\n and group membership of system files and commands match the vendor values.'\n desc 'Discretionary access control is weakened if a user or group has access permissions to system files and\n directories greater than the default.'\n desc 'rationale', ''\n desc 'check', %\n -rw-------. 1 root root 133 Jan 11 13:25 /etc/audisp/audisp-remote.conf\n If the file is more permissive than the default permissions, this is a finding.\n If the file is not owned by the default owner and is not documented with the Information System Security Officer\n (ISSO), this is a finding.\n If the file is not a member of the default group and is not documented with the Information System Security Officer\n (ISSO), this is a finding.>\n desc 'fix', \"\n Run the following command to determine which package owns the file:\n\n # rpm -qf \n\n Reset the user and group ownership of files within a package with the\n following command:\n\n #rpm --setugids \n\n\n Reset the permissions of files within a package with the following command:\n\n #rpm --setperms \n \"\n impact 0.7\n tag 'legacy': ['V-71849', 'SV-86473']\n tag 'severity': 'high'\n tag 'gtitle': 'SRG-OS-000257-GPOS-00098'\n tag 'satisfies': ['SRG-OS-000257-GPOS-00098', 'SRG-OS-000278-GPOS-00108']\n tag 'gid': 'V-204392'\n tag 'rid': 'SV-204392r646841_rule'\n tag 'stig_id': 'RHEL-07-010010'\n tag 'fix_id': 'F-36302r646840_fix'\n tag 'cci': ['CCI-001494', 'CCI-001496', 'CCI-002165', 'CCI-002235']\n tag nist: ['AU-9', 'AU-9 (3)', 'AC-3 (4)', 'AC-6 (10)']\n tag subsystems: ['permissions', 'package', 'rpm']\n tag 'host', 'container'\n\n if input('disable_slow_controls')\n describe \"This control consistently takes a long time to run and has been disabled\n using the disable_slow_controls attribute.\" do\n skip \"This control consistently takes a long time to run and has been disabled\n using the disable_slow_controls attribute. You must enable this control for a\n full accredidation for production.\"\n end\n else\n\n allowlist = input('rpm_verify_perms_except')\n\n misconfigured_packages = command('rpm -Va').stdout.split(\"\\n\")\n .select { |package| package[0..7].match(/M|U|G/) }\n .map { |package| package.match(/\\S+$/)[0] }\n\n if misconfigured_packages.empty?\n describe 'The list of rpm packages with permissions changed from the vendor values' do\n subject { misconfigured_packages }\n it { should be_empty }\n end\n else\n describe 'The list of rpm packages with permissions changed from the vendor values' do\n fail_msg = \"Files that have been modified from vendor-approved permissions but are not in the allowlist: #{(misconfigured_packages - allowlist).join(', ')}\"\n it 'should all appear in the allowlist' do\n expect(misconfigured_packages).to all(be_in(allowlist)), fail_msg\n end\n end\n end\n end\nend\n","source_location":{"ref":"./test/sample_data/controls-cookstyle/SV-204392.rb","line":1},"id":"SV-204392"}],"groups":[{"title":null,"controls":["SV-204474"],"id":"SV-204474.rb"},{"title":null,"controls":["SV-230385"],"id":"SV-230385.rb"},{"title":null,"controls":["SV-204392"],"id":"SV-204392.rb"}],"inputs":[],"sha256":"39d0b9b657c297fc336c249794b4c9bb5c8f7dfbb2ad83faf7723d79cae683dd","status_message":"","status":"loaded","generator":{"name":"inspec","version":"5.12.2"}} diff --git a/test/tests/control.spec.ts b/test/tests/control.spec.ts index 67c16c2b..4d766778 100644 --- a/test/tests/control.spec.ts +++ b/test/tests/control.spec.ts @@ -39,4 +39,10 @@ describe('The control functionality', () => { it('should correctly write the control structure using single quote string as suggested by cookstyle', () => { expect(generated2.replace(/\r/gi, '')).not.toEqual(expected2.replace(/\r/gi, '')); }) -}) \ No newline at end of file + + const generated3 = fs.readFileSync('test/sample_data/controls-test-results/SV-230385.rb','utf-8') + const expected3 = fs.readFileSync('test/sample_data/controls-cookstyle/SV-230385.rb', 'utf-8') + it('should extract describe block that includes keywords (e.g., tag, impact).', () => { + expect(generated3.replace(/\r/gi, '')).not.toEqual(expected3.replace(/\r/gi, '')); + }) +}) diff --git a/test/tests/getExistingDescribeFromControl.spec.ts b/test/tests/getExistingDescribeFromControl.spec.ts new file mode 100644 index 00000000..50fb72a5 --- /dev/null +++ b/test/tests/getExistingDescribeFromControl.spec.ts @@ -0,0 +1,28 @@ +import fs from 'fs' +import Control from '../../src/objects/control' +import {getExistingDescribeFromControl} from '../../src/index' + +describe('describe block extraction', () => { + const pathToTestCases = 'test/sample_data/controls-for-describe-tests' + let files = fs.readdirSync(pathToTestCases) + files = files.map(x => x.slice(0, x.length - 3)) // Remove the '.rb' from file names + test.each(files)('case involving %s', (file) => { + let expectedOutput = ' describe_block = nil' // Default expected output + const controlCode = fs.readFileSync(`${pathToTestCases}/${file}.rb`, 'utf-8') + const testControl = new Control({code: controlCode}) + const generatedOutput = getExistingDescribeFromControl(testControl) + + // Redefine variable `expectedOutput` for customized expected output, accordingly. + // Tip: use an escaped string for readability (e.g., https://www.browserling.com/tools/add-slashes). + if (file == 'comments') { + expectedOutput = ' # Commodo sed egestas egestas fringilla.\n # Ultricies tristique nulla aliquet enim. \n describe_block = nil\n # Volutpat consequat mauris nunc congue nisi.\n=begin \nPellentesque sit amet porttitor eget. Duis at tellus at urna. Pretium aenean \npharetra magna ac placerat vestibulum lectus mauris ultrices. Bibendum at \nvarius vel pharetra vel turpis nunc eget lorem. Ultrices mi tempus imperdiet \nnulla malesuada pellentesque elit eget gravida.\n=end\n\n # This comment is in the describe block.\n\n=begin\nThis is a multi-line comment in the describe block.\n\nVestibulum lorem sed risus ultricies tristique nulla. Interdum velit euismod \nin pellentesque massa. Et magnis dis parturient montes nascetur ridiculus mus \nmauris vitae. Augue lacus viverra vitae congue eu. Et ultrices neque ornare \naenean. Lectus urna duis convallis convallis tellus id interdum velit.\n=end\n\n describe_block = nil' + } else if (file == 'headers-in-describe') { + expectedOutput = ' describe_block = nil\n if describe_block\n impact 1.0\n tag \'headers\': \'in describe\'\n ref \'https://sample.com\'\n desc \'Amet dictum sit amet justo.\'\n end' + } else if (file == 'multi-line-describe-block') { + expectedOutput = ' describe \'Sed enim ut sem viverra. Elit pellentesque habitant morbi\n tristique senectus et netus et malesuada. At tempor commodo ullamcorper\n a lacus vestibulum.\' do\n describe_block = true\n end' + } else if (file == 'keywords-in-strings') { + expectedOutput = ' keyword_in_string = \'Lorem ipsum desc test control\'\n describe_block = nil' + } + expect(generatedOutput).toEqual(expectedOutput); + }); +});