Improve security and reliability of macOS updates

This commit introduces several improvements to the macOS update process,
primarily focusing on enhancing security and reliability:

- Add data integrity checks to ensure downloaded updates haven't been
  tampered with.
- Optimize update progress logging in `streamWithProgress` by limiting
  amount of logs during the download process.
- Improve resource management by ensuring proper closure of file
  read/write streams.
- Add retry logic with exponential back-off during file access to handle
  occassionally seen file system preparation delays on macOS.
- Improve decision-making based on user responses.
- Improve clarity and informativeness of log messages.
- Update error dialogs for better user guidance when updates fail to
  download, unexpected errors occur or the installer can't be opened.
- Add handling for unexpected errors during the update process.
- Move to asynchronous functions for more efficient operation.
- Move to scoped imports for better code clarity.
- Update `Readable` stream type to a more modern variant in Node.
- Refactor `ManualUpdater` for improved separation of concerns.
- Document the secure update process, and log directory locations.
- Rename files to more accurately reflect their purpose.
- Add `.DS_Store` in `.gitignore` to avoid unintended files in commits.
This commit is contained in:
undergroundwires
2023-12-04 18:28:43 +01:00
parent 25e23c89c3
commit 4765752ee3
14 changed files with 653 additions and 176 deletions

View File

@@ -0,0 +1,38 @@
import { createHash } from 'crypto';
import { createReadStream } from 'fs';
import { ElectronLogger } from '@/infrastructure/Log/ElectronLogger';
import { retryFileSystemAccess } from './RetryFileSystemAccess';
export async function checkIntegrity(
filePath: string,
base64Sha512: string,
): Promise<boolean> {
return retryFileSystemAccess(
async () => {
const hash = await computeSha512(filePath);
if (hash === base64Sha512) {
ElectronLogger.info(`Integrity check passed for file: ${filePath}.`);
return true;
}
ElectronLogger.warn([
`Integrity check failed for file: ${filePath}`,
`Expected hash: ${base64Sha512}, but found: ${hash}`,
].join('\n'));
return false;
},
);
}
async function computeSha512(filePath: string): Promise<string> {
try {
const hash = createHash('sha512');
const stream = createReadStream(filePath);
for await (const chunk of stream) {
hash.update(chunk);
}
return hash.digest('base64');
} catch (error) {
ElectronLogger.error(`Failed to compute SHA512 hash for file: ${filePath}`, error);
throw error; // Rethrow to handle it in the calling context if necessary
}
}