Indexer for SMVA save files
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
smva-indexer/updateable-launcher.mjs

84 lines
3.4 KiB

import {main as bakedInMain} from './indexer.mjs'
import {readFile} from 'fs/promises'
import {join} from "path"
import chalk from "chalk"
import parseDataUrl from "data-urls"
import publicKeyDataUrl from "./smva-indexer-release.pub.pem"
import envPaths from "env-paths"
import configName from "consts:configName"
import {signatureGuardStart, signatureGuardEnd, algorithm, absolutize} from "./signing-common.mjs"
import {createPublicKey, createVerify} from "crypto"
const publicKey = createPublicKey({
key: parseDataUrl(publicKeyDataUrl).body,
format: "pem"
})
async function validateSignature(input) {
if (!input.subarray(0, signatureGuardStart.length).equals(signatureGuardStart)) {
throw new Error(`File does not start with ${JSON.stringify(signatureGuardStart.toString())}`)
}
const signatureLength = input.subarray(signatureGuardStart.length).indexOf(signatureGuardEnd)
if (signatureLength === -1) {
throw new Error(`Signature does not end with ${JSON.stringify(signatureGuardEnd.toString())}`)
}
const verify = createVerify(algorithm)
const signedContent = input.subarray(signatureGuardStart.length + signatureLength + signatureGuardEnd.length)
verify.update(signedContent)
const signature = input.subarray(signatureGuardStart.length, signatureGuardStart.length + signatureLength).toString("utf-8")
if (!verify.verify(publicKey, signature, "base64")) {
throw new Error(`Signature was incorrect`)
}
}
async function tryFiles(paths) {
for (const path of paths) {
try {
const input = await readFile(path)
if (!process.env.SMVA_INDEXER_IGNORE_SIGNATURES) {
await validateSignature(input)
}
const {main: result} = await import(absolutize(path))
if (result !== bakedInMain) {
process.stderr.write(chalk.cyan(`Loaded code for ${chalk.cyanBright.bold(`v${result.version}`)} from ${chalk.cyanBright.bold(path)}\n`))
return result
}
} catch (ex) {
if (ex.code === "ENOENT" || ex.code === "EACCES") {
// That's okay, just move on to the next file
} else {
process.stderr.write(chalk.red(`Error while checking for an updated bundle at ${chalk.redBright.bold(path)}: ${chalk.redBright.bold(ex)}\n`))
}
}
}
// None of the files both existed AND were valid.
return null
}
const bundleFilename = "smva-indexer-bundle-signed.js"
async function start() {
const bundlesToTry = []
const cwdBundle = join(process.cwd(), bundleFilename)
if (!bundlesToTry.includes(cwdBundle) && !process.env.SMVA_INDEXER_IGNORE_CWD_BUNDLE) {
bundlesToTry.push(cwdBundle)
}
const besideBundle = join(module.path, bundleFilename)
if (!bundlesToTry.includes(besideBundle) && !process.env.SMVA_INDEXER_IGNORE_NEARBY_BUNDLE) {
bundlesToTry.push(besideBundle)
}
const configBundle = join(envPaths(configName).config, bundleFilename)
if (!bundlesToTry.includes(configBundle) && !process.env.SMVA_INDEXER_IGNORE_CONFIG_BUNDLE) {
bundlesToTry.push(configBundle)
}
const loadedMain = process.env.SMVA_INDEXER_IGNORE_ALL_BUNDLES ? null : await tryFiles(bundlesToTry);
await (loadedMain || bakedInMain)()
}
if (!global.main) {
global.main = true
start()
}
export const main = bakedInMain;