From d2d4e98e4fcd7223dcfd9ca68bd1ca711f8b8c38 Mon Sep 17 00:00:00 2001 From: William Oldham Date: Tue, 20 May 2025 23:21:14 +0100 Subject: [PATCH] feat(update-rotation): add better env validation and add checksum for idempotency --- .gitignore | 129 ++++++++++++++++++++++---------------------- update-rotation.mjs | 44 ++++++++++++++- 2 files changed, 109 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 65e9cf6..7e3dce6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,63 +1,66 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - -# custom -.vscode -dist -cdn/storage \ No newline at end of file +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# custom +.vscode +dist +cdn/storage +*.byaml +*.byaml.checksum +*.bak diff --git a/update-rotation.mjs b/update-rotation.mjs index 0c28c34..aaf9ae0 100644 --- a/update-rotation.mjs +++ b/update-rotation.mjs @@ -10,8 +10,24 @@ import xml from 'xml-js'; dotenv.config(); +function md5(input) { + return crypto.createHash('md5').update(input).digest('hex'); +} + +const BOSS_WIIU_AES_KEY_MD5_HASH = '5202ce5099232c3d365e28379790a919'; +const BOSS_WIIU_HMAC_KEY_MD5_HASH = 'b4482fef177b0100090ce0dbeb8ce977'; + const { PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY, PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY } = process.env; +if (!PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY || md5(PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY) !== BOSS_WIIU_AES_KEY_MD5_HASH) { + console.error('PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY is not set or does not match the expected value'); + process.exit(1); +} +if (!PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY || md5(PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY) !== BOSS_WIIU_HMAC_KEY_MD5_HASH) { + console.error('PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY is not set or does not match the expected value'); + process.exit(1); +} + const rl = readline.createInterface({ input: process.stdin, output: process.stdout @@ -27,12 +43,16 @@ const askQuestion = (question) => { const rootDir = import.meta.dirname; const sourceFile = path.join(rootDir, 'VSSetting.byaml'); +const checksumFile = path.join(rootDir, 'cdn/VSSetting.byaml.checksum'); const exists = await fs.exists(sourceFile); if (!exists) { - throw new Error('Source VSSetting.byaml file does not exist'); + console.error('Source VSSetting.byaml file does not exist'); + process.exit(1); } +const sourceFileContents = await fs.readFile(sourceFile); + const stat = await fs.stat(sourceFile); if (stat.mtime.toDateString() !== new Date().toDateString()) { const answer = await askQuestion(`The source file was not updated today (Updated ${stat.mtime.toDateString()}). Do you want to continue? (y/n) `); @@ -41,6 +61,20 @@ if (stat.mtime.toDateString() !== new Date().toDateString()) { } } +const checksumExists = await fs.exists(checksumFile); +const newChecksum = crypto.createHash('sha256').update(sourceFileContents).digest('hex'); +console.log(`Checksum for source file is ${newChecksum}`); +if (checksumExists) { + const checksum = await fs.readFile(checksumFile, 'utf8'); + + if (checksum === newChecksum) { + const answer = await askQuestion('The source file has not changed since the last run. Do you want to continue? (y/n) '); + if (answer.toLowerCase() !== 'y') { + process.exit(0); + } + } +} + const titles = [ 'bb6tOEckvgZ50ciH', 'rjVlM7hUXPxmYQJh', @@ -49,6 +83,11 @@ const titles = [ async function backupFile(filePath) { const copyFilePath = path.join(path.dirname(filePath), `${path.basename(filePath)}.bak`); + const exists = await fs.exists(filePath); + if (!exists) { + console.log(`File ${filePath} does not exist, skipping backup...`); + return; + } await fs.copyFile(filePath, copyFilePath); console.log(`Backup created of ${filePath} at ${copyFilePath}`); } @@ -113,3 +152,6 @@ for (const title of titles) { } rl.close(); + +console.log('All tasks completed successfully!'); +await fs.writeFile(checksumFile, newChecksum);