Skip to content
This repository was archived by the owner on Sep 27, 2024. It is now read-only.

Conversation

@matejak
Copy link
Contributor

@matejak matejak commented Oct 11, 2017

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.

@openscap-ci
Copy link

Can one of the admins verify this patch?

@matejak
Copy link
Contributor Author

matejak commented Oct 11, 2017

The easier part has been done - you should be able to save remediations from the main app window.

void toggleRuleResultsExpanded(bool checked);

/// Pops up a save dialog for a bash remediation
void genBash();
Copy link
Member

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.

Copy link
Member

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.

Copy link
Contributor Author

@matejak matejak Oct 11, 2017

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.

if (filename.isEmpty())
return;

int output_fd = open(filename.toUtf8(), O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0700);
Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mpreisler mpreisler changed the title Implement generation of remediation scripts. Generate bash and ansible remediation roles from profiles Oct 11, 2017
@mpreisler mpreisler added this to the 1.1.6 milestone Oct 11, 2017
@mpreisler
Copy link
Member

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.

@mpreisler
Copy link
Member

@openscap-ci add to whitelist

void toggleRuleResultsExpanded();
void toggleRuleResultsExpanded(bool checked);

/// Pops up a save dialog for a bash remediation
Copy link
Member

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());

Copy link
Member

@mpreisler mpreisler Oct 11, 2017

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.

Copy link
Contributor Author

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()
{
Copy link
Member

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.

Copy link
Contributor Author

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">
Copy link
Member

@mpreisler mpreisler Oct 11, 2017

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">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generate remediation role?

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);
Copy link
Member

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

const QString filename = QFileDialog::getSaveFileName(this,
QObject::tr("Save as Ansible playbook"),
QObject::tr("remediate-profile.yml"),
QObject::tr("ansible playbooks (*.yml)"), 0
Copy link
Member

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"

const QString filename = QFileDialog::getSaveFileName(this,
QObject::tr("Save as bash script"),
QObject::tr("remediate-profile.sh"),
QObject::tr("bash scripts (*.sh)"), 0
Copy link
Member

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"

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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error checking missing

if (filename.isEmpty())
return;

QFile output_file(filename);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code style (variable name)

{
const QString filename = QFileDialog::getSaveFileName(this,
QObject::tr("Save as bash script"),
QObject::tr("remediate-profile.sh"),
Copy link
Member

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.

Copy link
Contributor Author

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.

@mpreisler
Copy link
Member

mpreisler commented Oct 12, 2017

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.

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");
Copy link
Member

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.

* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Martin Preisler <mpreisle@redhat.com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ehm... nope :)

{
public:
RemediationSaverBase(QWidget* parentWindow, ScanningSession* session): mParentWindow(parentWindow), mScanningSession(session) {}
RemediationSaverBase(QWidget* parentWindow, ScanningSession* session): mParentWindow(parentWindow), mScanningSession(session) {}
Copy link
Member

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)
{
Copy link
Member

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");
Copy link
Member

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?

Copy link
Contributor Author

@matejak matejak Oct 12, 2017

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".

Copy link
Member

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

);
}

// TODO: Reuse code between generateBashRemediationRole and generateAnsibleRemediationRole
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outdated comment

if (filename.isEmpty())
return;

int result = SaveToFile(filename);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be const


#include "RemediationRoleSaver.h"

void RemediationSaverBase::SelectFilenameAndSaveRole()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

method name

}
}

int RemediationSaverBase::SaveToFile(const QString& filename)
Copy link
Member

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

return result;
}

QString RemediationSaverBase::guessFilenameStem()
Copy link
Member

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

{
public:
RemediationSaverBase(QWidget* parentWindow, ScanningSession* session):
mParentWindow(parentWindow), mScanningSession(session) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move ctor to cpp

remediationButtonMenu->addAction(genBashRemediation);
remediationButtonMenu->addAction(genAnsibleRemediation);
// remediationButtonMenu->addAction(mGenPuppetRemediation);
// remediationButtonMenu->addAction(mGenAnacondaRemediation);
Copy link
Member

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());
Copy link
Member

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.

@mpreisler
Copy link
Member

ACK

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants