Atomic Remediation with Exception Handling and Compliance Reporting - #1064
Conversation
… command fails on rollback failure.
| return true; | ||
| } | ||
|
|
||
| private String applyChange(String instanceId, String filename, String fileHash, Charset sourceEncoding, String originalContent, |
There was a problem hiding this comment.
Wrong indentation for this and various other method declarations
| pendingWrite.updatedBytes().length); | ||
| writeSourceFile(pendingWrite.filePath(), pendingWrite.updatedBytes(), pendingWrite.filename()); | ||
| } | ||
| } catch (SkipRemediationException e) { |
There was a problem hiding this comment.
To understand the exception handling in this method, developers need to check writeSourceFile to understand that this converts IOException to SkipRemediationException. Also, this doesn't roll back on any other exceptions (which are unlikely but not impossible), whereas other methods explicitly catch Exception.
| LOG.warn("Rolled back remediation {} changes for '{}' after write failure", instanceId, pendingWrite.filename()); | ||
| } catch (IOException rollbackException) { | ||
| LOG.error("Failed to roll back remediation {} changes for '{}'", instanceId, pendingWrite.filename(), rollbackException); | ||
| throw new AviatorTechnicalException("Failed to roll back remediation changes for '" + pendingWrite.filename() + |
There was a problem hiding this comment.
Given that this situation requires special processing (command termination instead of skipping single remediation), it would be better to use a dedicated exception type like RollbackRemediationException or a more generic TerminateRemediationProcessingException or something similar. This would avoid unintended command termination if any other code throws AviatorTechnicalException.
| logger.error("Skipping file '{}' as it resolves to a path outside the source directory (potential path traversal attack)", filename); | ||
| continue; | ||
| try { | ||
| if (processRemediation(remediation, sourceBasePath, fvdlMetadataResult, modifiedFiles)) { |
There was a problem hiding this comment.
See comment on applyRemediation method to have that method define flow and exception handling for applying individual remediations. Ultimate goal would be to have this method simply do the following for every individual remediation, without try/catch block:
if (processRemediation(...)) {
appliedRemediations++;
}
| return new RemediationMetric(totalRemediations, appliedRemediations, skippedRemediations, modifiedFiles, skippedByReason); | ||
| } | ||
|
|
||
| private boolean processRemediation(Element remediation, Path sourceBasePath, FvdlMetadataResult fvdlMetadataResult, |
There was a problem hiding this comment.
Ideally, to better document the overall process for applying an individual remediation, and to centralize exception handling, this method should effectively do something like the following:
private boolean processRemediation(...) {
try {
var pendingWrites = prepareFileChanges(...); // May throw RemediationSkipExceptions only; either explicitly or through generic try/catch to indicate prepare error. If this already reads source files, result object can include both original contents (for rollback if needed) and the changes to be applied, otherwise original file contents may be collected before updating each source file in commitChanges method.
try {
commitChanges(pendingWrites); // May throw RemediationCommitException only, listing any files that were already updated
return true; // Indicate to caller that remediation was applied successfully
} catch ( RemediationCommitException e ) {
rollBackChanges(e.getRollbacks()); // Throws exception in case rollback was unsuccessful (ideally a dedicated exception type), causing command termination. RemediationCommitException::getRollbacks returns file paths and original contents for rollback.
}
} catch (RemediationSkipException e) {
// Log warning, update skippedByReason
}
return false; // Indicate to caller that remediation was not applied successfully
}
Or alternatively, instead of boolean to report status, just propagate exceptions to the caller, i.e.:
private void processRemediation(...) {
var pendingWrites = prepareFileChanges(...); // May throw RemediationSkipExceptions only; either explicitly or through generic try/catch to indicate prepare error. If this already reads source files, result object can include both original contents (for rollback if needed) and the changes to be applied, otherwise original file contents may be collected before updating each source file in commitChanges method.
try {
commitChanges(pendingWrites); // May throw RemediationCommitException only, listing any files that were already updated
} catch ( RemediationCommitException e ) {
rollBackChanges(e.getRollbacks()); // Throws exception in case rollback was unsuccessful (ideally a dedicated exception type), causing command termination. RemediationCommitException::getRollbacks returns file paths and original contents for rollback.
throw new RemediationSkipException(...); // Indicate remediation was (successfully) rolled back due to errors
}
}
With the latter, processRemediationsXML method would only catch RemediationSkipException to log warning & update skippedByReason, all other exceptions (like error during rollback) would result in command termination.
The above likely requires more work, main point is that it clearly encapsulates both processing (prepare, commit, rollback if needed) and exception handling (explicit skip reasons, technical errors, rollback errors, ...).
For some of these exception types, it may make sense to declare them as checked exceptions instead on RuntimeExceptions, as they are integral part of the overall workflow, and to explicitly document what exceptions may be thrown by each method.
…xception handling,fixed indentation
Note: The PR should be merged in dev/v3.x
Implement a robust remediation framework that ensures remediation actions are applied atomically, with comprehensive exception handling and detailed reporting. This approach minimizes partial updates, improves reliability, and provides clear visibility into remediation outcomes, failures, and compliance status.