-
Notifications
You must be signed in to change notification settings - Fork 71
Generate bash and ansible remediation roles from profiles #143
Generate bash and ansible remediation roles from profiles #143
Conversation
|
Can one of the admins verify this patch? |
|
The easier part has been done - you should be able to save remediations from the main app window. |
include/MainWindow.h
Outdated
| void toggleRuleResultsExpanded(bool checked); | ||
|
|
||
| /// Pops up a save dialog for a bash remediation | ||
| void genBash(); |
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.
write a better method name please, it's worth it for self-documentation and editors auto-complete anyway.
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.
also, can't we have just one method that gets "template" passed in? that way we can add even more remediation options easily later on.
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 think that it is unavoidable to have multiple methods as one menu item requires one slot method. As noted in the code, there is a potential for code reuse between those methods. I am thinking of polymorphism and I would postpone this until the remediation from results is implemented, since there could be some code reuse from this side as well.
src/MainWindow.cpp
Outdated
| if (filename.isEmpty()) | ||
| return; | ||
|
|
||
| int output_fd = open(filename.toUtf8(), O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0700); |
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 at all possible use Qt abstractions, AFAIK one of them gives you the fd.
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.
|
To avoid confusing users even more we have to stick to consistent terms. We use "remediation roles" in SCAP Security Guide and I think we should stick to it across all projects. |
|
@openscap-ci add to whitelist |
include/MainWindow.h
Outdated
| void toggleRuleResultsExpanded(); | ||
| void toggleRuleResultsExpanded(bool checked); | ||
|
|
||
| /// Pops up a save dialog for a bash remediation |
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.
since we talk about rule results just above this, let's make sure the comment mentions that these roles are generated for the currently selected profile, not for results
|
|
||
| // start centered | ||
| move(QApplication::desktop()->screen()->rect().center() - rect().center()); | ||
|
|
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 all of this really be done at the end of the ctor? Could you please improve the variable names? mGenMenu is not even a member and it starts with m. And the variable name is not descriptive.
Same for the other variables.
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 basically repeated what I have seen in the ResultsViewer constructor. I don't know where else to put it. It seems that the pattern here is that the appearance of the form is fully determined after the ctor is executed, so from this POV, it makes sense to group the stuff that defines UI in there.
|
|
||
| // TODO: Reuse code between generateBashRemediationRole and generateAnsibleRemediationRole | ||
| void MainWindow::generateBashRemediationRole() | ||
| { |
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.
refactor this into a method that takes the short name "bash remediation role", extension ".sh" and OpenSCAP remediation template "urn:xccdf:...". Then the two generate functions can just call that. Shorter code is better.
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 took slightly different approach, so I hope you like it too as it has the same effect.
| </item> | ||
| <item> | ||
| <widget class="QPushButton" name="genRemediationButton"> | ||
| <property name="toolTip"> |
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.
typo. again, please use consistent terms. It's a remediation role. It contains a fix for every single rule in the profile that has a fix.
| <property name="toolTip"> | ||
| <string>Generate remediations of all possible failiures that the selected profile can cover.</string> | ||
| </property> | ||
| <property name="text"> |
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.
Generate remediation role?
src/MainWindow.cpp
Outdated
| QFile output_file(filename); | ||
| output_file.open(QIODevice::WriteOnly); | ||
| struct xccdf_session * session = mScanningSession->getXCCDFSession(); | ||
| struct xccdf_policy *policy = xccdf_session_get_xccdf_policy(session); |
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.
code style for the above 2 lines. it sucks but code style in S-W is different from openscap
src/MainWindow.cpp
Outdated
| const QString filename = QFileDialog::getSaveFileName(this, | ||
| QObject::tr("Save as Ansible playbook"), | ||
| QObject::tr("remediate-profile.yml"), | ||
| QObject::tr("ansible playbooks (*.yml)"), 0 |
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.
file format is singular: "ansible playbook", not "playbooks"
src/MainWindow.cpp
Outdated
| const QString filename = QFileDialog::getSaveFileName(this, | ||
| QObject::tr("Save as bash script"), | ||
| QObject::tr("remediate-profile.sh"), | ||
| QObject::tr("bash scripts (*.sh)"), 0 |
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.
file format is singular: "bash script", not "scripts"
src/MainWindow.cpp
Outdated
| output_file.open(QIODevice::WriteOnly); | ||
| struct xccdf_session * session = mScanningSession->getXCCDFSession(); | ||
| struct xccdf_policy *policy = xccdf_session_get_xccdf_policy(session); | ||
| xccdf_policy_generate_fix(policy, NULL, "urn:xccdf:fix:script:sh", output_file.handle()); |
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.
error checking missing
src/MainWindow.cpp
Outdated
| if (filename.isEmpty()) | ||
| return; | ||
|
|
||
| QFile output_file(filename); |
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.
code style (variable name)
src/MainWindow.cpp
Outdated
| { | ||
| const QString filename = QFileDialog::getSaveFileName(this, | ||
| QObject::tr("Save as bash script"), | ||
| QObject::tr("remediate-profile.sh"), |
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.
Could we please base the suggested file name on the benchmark and profile ID? that would increase UX because many times I can just click save without typing anything.
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 keep this in mind, but I have postponed it. Do you have any idea what is a good form? I am not sure about benchmark IDs, but those profile IDs alone are quite long.
|
The class doesn't follow code conventions and more importantly I think that's a sign of over-engineering. The class doesn't really hold or remember any state, it just runs something. In this case I think we should just use a function, it's going to be easier and shorter. EDIT: I am willing to compromise on the class if you like that approach, I wouldn't implement it like that but it's fine by me. |
include/RemediationRoleSaver.h
Outdated
| mSaveMessage = QObject::tr("Save remediation role as a bash script"); | ||
| mFiletypeExtension = "sh"; | ||
| mFiletypeTemplate = QObject::tr("bash script (*.%1)"); | ||
| mFixTemplate = QObject::tr("urn:xccdf:fix:script:sh"); |
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.
use initialization lists, do this in the cpp file, not in the header.
src/RemediationRoleSaver.cpp
Outdated
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| * | ||
| * Authors: | ||
| * Martin Preisler <mpreisle@redhat.com> |
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.
ehm... nope :)
include/RemediationRoleSaver.h
Outdated
| { | ||
| public: | ||
| RemediationSaverBase(QWidget* parentWindow, ScanningSession* session): mParentWindow(parentWindow), mScanningSession(session) {} | ||
| RemediationSaverBase(QWidget* parentWindow, ScanningSession* session): mParentWindow(parentWindow), mScanningSession(session) {} |
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.
indents borked
|
|
||
|
|
||
| BashRemediationSaver::BashRemediationSaver(QWidget* parentWindow, ScanningSession* session):RemediationSaverBase(parentWindow, session) | ||
| { |
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.
split the line above into multiple lines, break after : makes it more readable, indent the initialization list items and keep them one per line.
| mSaveMessage = QObject::tr("Save remediation role as a bash script"); | ||
| mFiletypeExtension = "sh"; | ||
| mFiletypeTemplate = QObject::tr("bash script (*.%1)"); | ||
| mFixTemplate = QObject::tr("urn:xccdf:fix:script:sh"); |
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.
any reason why this is not initialized in the initialization list?
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.
It belongs to the base class and the base class doesn't know what should the content be.
Having it in the cpp file like this has the added benefit that If there is a new format of remediation rules, the person who will implement it will immediately know what is the meaning of each individual string thanks to the fact that it is assigned to the variable as opposed to being specified as "the 3rd argument to the constructor".
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.
ah, ok, I didn't realize you are setting parent class members in the ctor
src/MainWindow.cpp
Outdated
| ); | ||
| } | ||
|
|
||
| // TODO: Reuse code between generateBashRemediationRole and generateAnsibleRemediationRole |
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.
outdated comment
src/RemediationRoleSaver.cpp
Outdated
| if (filename.isEmpty()) | ||
| return; | ||
|
|
||
| int result = SaveToFile(filename); |
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.
can be const
src/RemediationRoleSaver.cpp
Outdated
|
|
||
| #include "RemediationRoleSaver.h" | ||
|
|
||
| void RemediationSaverBase::SelectFilenameAndSaveRole() |
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.
method name
src/RemediationRoleSaver.cpp
Outdated
| } | ||
| } | ||
|
|
||
| int RemediationSaverBase::SaveToFile(const QString& filename) |
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.
method name doesn't follow coding style
src/RemediationRoleSaver.cpp
Outdated
| return result; | ||
| } | ||
|
|
||
| QString RemediationSaverBase::guessFilenameStem() |
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.
this method can be const
include/RemediationRoleSaver.h
Outdated
| { | ||
| public: | ||
| RemediationSaverBase(QWidget* parentWindow, ScanningSession* session): | ||
| mParentWindow(parentWindow), mScanningSession(session) {} |
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.
move ctor to cpp
src/MainWindow.cpp
Outdated
| remediationButtonMenu->addAction(genBashRemediation); | ||
| remediationButtonMenu->addAction(genAnsibleRemediation); | ||
| // remediationButtonMenu->addAction(mGenPuppetRemediation); | ||
| // remediationButtonMenu->addAction(mGenAnacondaRemediation); |
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.
the puppet command probably makes sense but anaconda roles don't have a use-case. I suggest to uncomment what's supposed to be there and remove anaconda action for good.
| outputFile.open(QIODevice::WriteOnly); | ||
| struct xccdf_session* session = mScanningSession->getXCCDFSession(); | ||
| struct xccdf_policy* policy = xccdf_session_get_xccdf_policy(session); | ||
| int result = xccdf_policy_generate_fix(policy, NULL, mFixTemplate.toUtf8(), outputFile.handle()); |
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.
since we are writing C++ we should handle C errors where we use C and only there IMO. Let's check the error here and raise an exception. Leaking error codes outside of methods makes it a pain to use the API.
|
ACK |
Before this PR, S-W was able to remediate on the fly or remediate after the check using
oscap.This PR aims to enable users to control the remediation by giving them access to remediation scripts (that they can edit and execute by themselves).
EDIT by @mpreisler: Let's only do generate from profiles here and generate from results can be in a separate PR.