-
Notifications
You must be signed in to change notification settings - Fork 11
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
Adjust SSP Viewer for FedRAMP template #140
Conversation
To fill negative space and limit word wrap, stylistic adjustments have been made to OSCALSystemCharacteristics and OSCALSystemImplementation.
The FedRAMP SSP template provides a different format to how statement set-parameters are provide than seen in NIST's SSPs. Instead of having set-parameters provided on each statement of a control, they are provided by only the initial statement of a control in this SSP template. Control and underlying components have been reworked to fit this description.
src/components/OSCALControl.js
Outdated
@@ -61,6 +61,21 @@ export default function OSCALControl(props) { | |||
); | |||
} | |||
|
|||
// When FedRAMP, retrieve the set-parameters from the initial implementation request statement | |||
let firstImplReqStatement = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we just pulling the first of each here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this sample it contains the set-parameters
only in the first (initial) implemented requirement statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but we don't have guaranteed ordering here.
What I think we're really looking for are set-parameters
at the 'top-level' of the control, so within ac-1_smt
, which won't necessarily be the first statement in the array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh okay, that makes more sense. Maybe instead here, we should find the 'top-level' of the control with an ends with "_smt" check?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like that, we may already be doing something similar elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added an endsWith check when searching through the array of implReqStatements
for now. I haven't found a place where we're doing something similar but will continue looking.
src/components/OSCALControl.js
Outdated
} | ||
}); | ||
} | ||
// NOTE: The top-level by-component is the first element in the by-components array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is an accurate statement.
Each component
in the by-components
array represents how each component address the control.
I believe OSCALContorlImplementationImplReq
sends in a componentId
that we should check for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was a little hesitant to include that part as well. Grabbing the first array is definitely not the best practice here as this doesn't seem to be a consistent pattern. Also, thanks for clarifying on this. Your right about OSCALControlImplementationImplReq
passing componentId
as a prop. I can make a by-component
id match check as well - I'm not seeing it elsewhere.
To help with readability and reusability of code, the implemented requirements set-parameters fetch has been made into a seperate method in OSCALControl.
I chose to make the set-parameter fetch into a separate function in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good so far! This does not have to prevent an approval. I just had a few potential things to look into.
src/components/OSCALControl.js
Outdated
* @param {Object} implReqStatements | ||
* @param {String} componentId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would a brief description of these two parameters be useful here?
src/components/OSCALControl.js
Outdated
if (implReqStatements) { | ||
// NOTE: The top level statement ends with "_smt" | ||
topLevelImplReqStatement = implReqStatements.find((statement) => | ||
statement["statement-id"].endsWith("_smt") | ||
); | ||
} | ||
// NOTE: The top-level by-component has a matching uuid to the componentId | ||
let topLevelByComp = null; | ||
if (topLevelImplReqStatement?.["by-components"]) { | ||
topLevelByComp = topLevelImplReqStatement["by-components"].find( | ||
(byComp) => byComp["component-uuid"] === componentId | ||
); | ||
} | ||
return topLevelByComp?.["set-parameters"] || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could be wrong here, but is it possible to make this into just a pipe of find().find()
or something along those lines? It seems like there could be a cleaner way of doing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Viewer looks as specified locally.
I would like to see @tuckerzp 's comments implemented, but other than that looks great.
src/components/OSCALControl.js
Outdated
* @param {String} componentId | ||
* @returns An array of set-parameters | ||
*/ | ||
function getImplReqSetParameters(implReqStatements, componentId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this entire function be pushed down to OSCALControlProse.js
since that code is already responsible for finding the parameters and has the componentId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will push the function to OSCALControlProse.js
Clean up code in getImplReqSetParameters by creating a find.find pipe and removing the extra lines no longer needed. Add brief description to the parameters of getImplReqSetParameters to improve understanding of their purpose.
Move getImplReqSetParameters from OSCALControl to OSCALControlProse, as OSCALControlProse already finds the parameters and has the componentId.
src/components/OSCALControl.js
Outdated
@@ -61,6 +62,12 @@ export default function OSCALControl(props) { | |||
); | |||
} | |||
|
|||
// Retrieve implemented requirements set-parameters | |||
const setParams = getImplReqSetParameters( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant the function and the execution of that function could be moved to OSCALControlProse
.
There shouldn't be any need to evaluate here and pass the results as implReqSetParameters
all the way down to OSCALControlProse
where it's actually used, I think we should be able to just call from within OSCALControlProse
.
Move getImplReqSetParameters to OSCALControlProse file, as the result is only used in that file. Modified OSCALControl and OSCALControlPart to no longer use implReqSetParameters, as it is not used in those files. This improve readability and understanding of the code and where results of functions are used.
Move getImplReqSetParameters to top of file, as it was previously being used before it was defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts but not really required.
src/components/OSCALControlProse.js
Outdated
// Get the top-level implemented requirement statement | ||
const topLevelByComp = implReqStatements | ||
?.find((statement) => statement["statement-id"].endsWith("_smt")) | ||
?.["by-components"]?.find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?.["by-components"]?.find( | |
?.["by-components"] | |
?.find( |
Would the linter allow this? Makes the chain a little more "obvious"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried locally but whenever I run npm run lint
, the suggested change is fixed to what it originally looked like so I don't think so.
src/components/OSCALControlProse.js
Outdated
// Locate set-parameters when found in the by-component | ||
if (statementByComponent?.["set-parameters"]) { | ||
foundParameterSetting = statementByComponent["set-parameters"].find( | ||
(parameterSetting) => parameterSetting["param-id"] === parameterId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're using this twice (once per branch), we should move it to it's own function within getParameterValue
.
// This assumes that `parameterId` from `getParameterValue` is in scope
function parameterHasGivenId(parameterSetting) {
return parameterSetting["param-id"] === parameterId;
}
And then you can use it as:
if (statementByComponent?.["set-parameters"]) {
foundParameterSetting = statementByComponent["set-parameters"].find(parameterHasGivenId);
} else if (implReqSetParameters) {
foundParameterSetting = implReqSetParameters.find(parameterHasGivenId);
} else {
return null;
}
But I'd also suggest that we've got quite a bit of repetition there still (between the if and the actual clause). And we may want to consider also doing something like:
const setParameters = () => statementByComponents?.["set-parameters"] || implReqSetParameters;
const foundParameterSetting = setParameters()?.find(parameterHasGivenId);
And since the next check is if (!foundParameterSetting?.values) { return null; }
we shouldn't need to modify any code. But we could do a if (!foundParameterSetting) { return null; }
if we really wanted to.
This has the benefits of not only reducing repetition but also keeping more things const
. It's also one less null
that we really need to worry about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made all recommended changes except the change in the conditional because we do foundParameterSetting.values.toString()
right after the check and if foundParameterSetting.values
is null, we would get an error.
src/components/OSCALControlProse.js
Outdated
const topLevelByComp = implReqStatements | ||
?.find((statement) => statement["statement-id"].endsWith("_smt")) | ||
?.["by-components"]?.find( | ||
(byComp) => byComp["component-uuid"] === componentId | ||
); | ||
return topLevelByComp?.["set-parameters"] || null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const topLevelByComp = implReqStatements | |
?.find((statement) => statement["statement-id"].endsWith("_smt")) | |
?.["by-components"]?.find( | |
(byComp) => byComp["component-uuid"] === componentId | |
); | |
return topLevelByComp?.["set-parameters"] || null; | |
return implReqStatements | |
?.find((statement) => statement["statement-id"].endsWith("_smt")) | |
?.["by-components"]?.find( | |
(byComp) => byComp["component-uuid"] === componentId | |
) | |
?.["set-parameters"] || null; |
Any reason to keep the temporary value variable there?
Clean up code in OSCALControlProse to remove duplication, make code easier to read and understand, and keep more variables const.
Clean up OSCALControlProse file to remove a function definition and convert it to an expression, as the function is only used once and is no longer needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjustments look good
Adjustments have been made in the visual display of the FedRAMP SSP template, importing a profile, and displaying statements.
Visually, columns in System Characteristics and System Implementation have been adjusted in placement and size to lessen word wrap and fill more of the negative space. While making visual adjustments, the placement of other elements on screen were maintained.
After resolving the
import-profile
xml link inOSCALSspResolver
, there was a need to rework how statements are fetched for the implementation controls to display correctly. This FedRAMP SSP template provided theset-parameters
in the first/overarching statement, rather than each individual statement for the controls. For example, theset-parameters
are listed purely in "ac-1_smt" versus being listed in something like "ac-1_smt.a" or "ac-1_smt.b.1" in NIST's SSPs.OSCALControl
now tries to grab these statements (which are null if provided like NIST's statements), then passes them to underlying components. With this added feature, the SSP Viewer has no problem handling controls from both NIST and FedRAMP.Testing
Use the FedRAMP SSP template when running the SSP Viewer.