Search before asking
Fesod version
main
JDK version
any
Operating system
Linux
Steps To Reproduce
When an xlsx is written with .password(...) and the encryption step fails, the caller gets an exception, but the workbook has already been written unencrypted. This happens both when writing to an OutputStream and when writing to a File.
OutputStream: make encryption fail by pointing the temp directory at a path that can't be created:
File blocker = File.createTempFile("blocker", ".tmp");
String original = FileUtils.getTempFilePrefix();
FileUtils.setTempFilePrefix(blocker.getAbsolutePath() + File.separator + "sub" + File.separator);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
FesodSheet.write(out, DemoData.class)
.excelType(ExcelTypeEnum.XLSX)
.password("secret")
.sheet("s")
.doWrite(data); // throws
} finally {
FileUtils.setTempFilePrefix(original);
}
// out holds data readable with no password
File: make the target read-only once its output stream is open, so the encrypt-in-place step can't reopen it (run as a non-root user):
File target = new File("secret.xlsx");
FesodSheet.write(target, DemoData.class)
.excelType(ExcelTypeEnum.XLSX)
.password("secret")
.registerWriteHandler(new WorkbookWriteHandler() {
@Override
public void afterWorkbookDispose(WorkbookWriteHandlerContext context) {
target.setReadOnly();
}
})
.sheet("s")
.doWrite(data); // throws
// secret.xlsx is a plain xlsx, readable with no password
These setups only force the failure. For a File, any error in the encrypt-in-place step triggers it (the target can't be reopened, e.g. locked by another process; or encryption runs out of memory on a large workbook). For an OutputStream, it needs the encryption temp file to fail to open while the rest of the write succeeded, e.g. under file-descriptor exhaustion. Rare either way, but when it happens, the caller gets unprotected data after asking for a password.
Current Behavior
The exception is thrown, but a complete, unencrypted xlsx is in the stream or on disk, and it reads fine without a password. If the caller has already sent the response or keeps the file, the data goes out without protection.
Expected Behavior
The write fails and leaves no unencrypted output: nothing is written to the stream, and no file is left on disk. The caller asked for password protection, so an exception with no output seems like the only safe result.
Anything else?
Cause: WriteContextImpl#finish(boolean) saves each step's exception and still runs the remaining steps:
- When
doOutputStreamEncrypt07() throws, isOutputStreamEncrypt stays false, so the next block writes the plain workbook to the stream, because writeExcel is still true.
- When
doFileEncrypt07() throws, nothing removes the file that was already written unencrypted.
This doesn't look intended. When EasyExcel added encryption (alibaba/easyexcel@44068c94), that catch threw straight away, so nothing was written to the stream. alibaba/easyexcel@2c8918be, an unrelated fix, changed every catch in finish() to save the exception and carry on, so the later close steps would still run. From then on, an encryption failure falls through to the unencrypted write. The code came into Fesod unchanged.
Proposed fix:
OutputStream: when doOutputStreamEncrypt07() fails, set writeExcel = false, so the unencrypted workbook is not written. The workbook and streams are still closed.
File: when doFileEncrypt07() fails, delete the target file. Nothing is lost, because the builder already truncated the file when it opened it.
Are you willing to submit a PR?
Search before asking
Fesod version
main
JDK version
any
Operating system
Linux
Steps To Reproduce
When an xlsx is written with
.password(...)and the encryption step fails, the caller gets an exception, but the workbook has already been written unencrypted. This happens both when writing to anOutputStreamand when writing to aFile.OutputStream: make encryption fail by pointing the temp directory at a path that can't be created:File: make the target read-only once its output stream is open, so the encrypt-in-place step can't reopen it (run as a non-root user):These setups only force the failure. For a
File, any error in the encrypt-in-place step triggers it (the target can't be reopened, e.g. locked by another process; or encryption runs out of memory on a large workbook). For anOutputStream, it needs the encryption temp file to fail to open while the rest of the write succeeded, e.g. under file-descriptor exhaustion. Rare either way, but when it happens, the caller gets unprotected data after asking for a password.Current Behavior
The exception is thrown, but a complete, unencrypted xlsx is in the stream or on disk, and it reads fine without a password. If the caller has already sent the response or keeps the file, the data goes out without protection.
Expected Behavior
The write fails and leaves no unencrypted output: nothing is written to the stream, and no file is left on disk. The caller asked for password protection, so an exception with no output seems like the only safe result.
Anything else?
Cause:
WriteContextImpl#finish(boolean)saves each step's exception and still runs the remaining steps:doOutputStreamEncrypt07()throws,isOutputStreamEncryptstaysfalse, so the next block writes the plain workbook to the stream, becausewriteExcelis stilltrue.doFileEncrypt07()throws, nothing removes the file that was already written unencrypted.This doesn't look intended. When EasyExcel added encryption (alibaba/easyexcel@44068c94), that
catchthrew straight away, so nothing was written to the stream. alibaba/easyexcel@2c8918be, an unrelated fix, changed everycatchinfinish()to save the exception and carry on, so the later close steps would still run. From then on, an encryption failure falls through to the unencrypted write. The code came into Fesod unchanged.Proposed fix:
OutputStream: whendoOutputStreamEncrypt07()fails, setwriteExcel = false, so the unencrypted workbook is not written. The workbook and streams are still closed.File: whendoFileEncrypt07()fails, delete the target file. Nothing is lost, because the builder already truncated the file when it opened it.Are you willing to submit a PR?