Use AthShield for mod verification

Implemented mod verification using AthShield when enabled. Added detailed mod identity extraction and validation logic for better integrity checks. Added logs for each verification step and fallback to hash-based identity if the manifest is missing.

Grande ligne : quand tu actives ath shield alors il utilise le système Athena's Shield.
This commit is contained in:
Sandro Soria 2024-10-24 16:10:14 +02:00
parent 5f3e229360
commit 886b29e356

View File

@ -66,96 +66,122 @@ async function dlAsync(login = true) {
} }
// --------- Mod Verification Logic --------- // --------- Mod Verification Logic ---------
const modsDir = path.join(ConfigManager.getDataDirectory(), 'instances', serv.rawServer.id, 'mods')
// Check if mods directory exists, if not, create it if (athShield.status) {
if (!fs.existsSync(modsDir)) { loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.usingAthShield'))
fs.mkdirSync(modsDir, { recursive: true })
}
const distroMods = {} const modsDir = path.join(ConfigManager.getDataDirectory(), 'instances', serv.rawServer.id, 'mods')
const mdls = serv.modules
// Populate expected mod identities and log them // Check if mods directory exists, if not, create it
mdls.forEach(mdl => { if (!fs.existsSync(modsDir)) {
if (mdl.rawModule.name.endsWith('.jar')) { fs.mkdirSync(modsDir, {recursive: true})
const modPath = path.join(modsDir, mdl.rawModule.name)
const modIdentity = mdl.rawModule.identity || mdl.rawModule.MD5
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.distributionIdentityError', {'moduleName': mdl.rawModule.name, 'moduleIdentity': modIdentity}))
distroMods[modPath] = modIdentity
}
})
// Function to extract mod identity from the jar file
const extractModIdentity = (filePath) => {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modIdentityExtraction', {'filePath': filePath}))
const zip = new AdmZip(filePath)
const manifestEntry = zip.getEntry('META-INF/MANIFEST.MF')
if (manifestEntry) {
const manifestContent = manifestEntry.getData().toString('utf8')
const lines = manifestContent.split('\n')
const identityLine = lines.find(line => line.startsWith('Mod-Id:') || line.startsWith('Implementation-Title:'))
if (identityLine) {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.manifestIdentityFound', {'filePath': filePath, 'identityLine': identityLine}))
return identityLine.split(':')[1].trim()
}
} }
// Fall back to a hash if no identity is found const distroMods = {}
const fileBuffer = fs.readFileSync(filePath) const mdls = serv.modules
const hashSum = crypto.createHash('md5') // Use MD5 to match the distribution configuration
hashSum.update(fileBuffer)
const hash = hashSum.digest('hex')
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.identityNotFoundInManifest', {'filePath': filePath, 'hash': hash}))
return hash
}
// Validate mods function // Populate expected mod identities and log them
const validateMods = () => { mdls.forEach(mdl => {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.startingModValidation')) if (mdl.rawModule.name.endsWith('.jar')) {
const installedMods = fs.readdirSync(modsDir) const modPath = path.join(modsDir, mdl.rawModule.name)
let valid = true const modIdentity = mdl.rawModule.identity || mdl.rawModule.MD5
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.distributionIdentityError', {
'moduleName': mdl.rawModule.name,
'moduleIdentity': modIdentity
}))
distroMods[modPath] = modIdentity
}
})
for (let mod of installedMods) { // Function to extract mod identity from the jar file
const modPath = path.join(modsDir, mod) const extractModIdentity = (filePath) => {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modIdentityExtraction', {'filePath': filePath}))
const zip = new AdmZip(filePath)
const manifestEntry = zip.getEntry('META-INF/MANIFEST.MF')
// Skip validation for mods in the excluded list if (manifestEntry) {
if (EXCLUDED_MODS.includes(mod)) { const manifestContent = manifestEntry.getData().toString('utf8')
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationBypassed', {'mod': mod})) const lines = manifestContent.split('\n')
continue const identityLine = lines.find(line => line.startsWith('Mod-Id:') || line.startsWith('Implementation-Title:'))
if (identityLine) {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.manifestIdentityFound', {
'filePath': filePath,
'identityLine': identityLine
}))
return identityLine.split(':')[1].trim()
}
} }
const expectedIdentity = distroMods[modPath] // Fall back to a hash if no identity is found
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.validatingMod', {'mod': mod})) const fileBuffer = fs.readFileSync(filePath)
const hashSum = crypto.createHash('md5') // Use MD5 to match the distribution configuration
hashSum.update(fileBuffer)
const hash = hashSum.digest('hex')
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.identityNotFoundInManifest', {
'filePath': filePath,
'hash': hash
}))
return hash
}
if (expectedIdentity) { // Validate mods function
const modIdentity = extractModIdentity(modPath) const validateMods = () => {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.expectedAndCalculatedIdentity', {'expectedIdentity': expectedIdentity, 'mod': mod, 'modIdentity': modIdentity})) loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.startingModValidation'))
const installedMods = fs.readdirSync(modsDir)
let valid = true
if (modIdentity !== expectedIdentity) { for (let mod of installedMods) {
loggerLanding.error(Lang.queryJS('landing.dlAsync.AthShield.modIdentityMismatchError', {'mod': mod, 'expectedIdentity': expectedIdentity, 'modIdentity': modIdentity})) const modPath = path.join(modsDir, mod)
// Skip validation for mods in the excluded list
if (EXCLUDED_MODS.includes(mod)) {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationBypassed', {'mod': mod}))
continue
}
const expectedIdentity = distroMods[modPath]
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.validatingMod', {'mod': mod}))
if (expectedIdentity) {
const modIdentity = extractModIdentity(modPath)
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.expectedAndCalculatedIdentity', {
'expectedIdentity': expectedIdentity,
'mod': mod,
'modIdentity': modIdentity
}))
if (modIdentity !== expectedIdentity) {
loggerLanding.error(Lang.queryJS('landing.dlAsync.AthShield.modIdentityMismatchError', {
'mod': mod,
'expectedIdentity': expectedIdentity,
'modIdentity': modIdentity
}))
valid = false
break
}
} else {
loggerLanding.warn(Lang.queryJS('landing.dlAsync.AthShield.expectedIdentityNotFound', {'mod': mod}))
valid = false valid = false
break break
} }
} else {
loggerLanding.warn(Lang.queryJS('landing.dlAsync.AthShield.expectedIdentityNotFound', {'mod': mod}))
valid = false
break
} }
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationCompleted'))
return valid
} }
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.modValidationCompleted')) // Perform mod validation before proceeding
return valid if (!validateMods()) {
const errorMessage = Lang.queryJS('landing.dlAsync.AthShield.invalidModsDetectedMessage', {'folder': dataPath})
loggerLanding.error(errorMessage)
showLaunchFailure(errorMessage, null)
return
}
} else {
loggerLanding.info(Lang.queryJS('landing.dlAsync.AthShield.notUsingAthShield'))
} }
// Perform mod validation before proceeding
if (!validateMods()) {
const errorMessage = Lang.queryJS('landing.dlAsync.AthShield.invalidModsDetectedMessage', {'folder': dataPath})
loggerLanding.error(errorMessage)
showLaunchFailure(errorMessage, null)
return
}
// --------- End of Mod Verification Logic --------- // --------- End of Mod Verification Logic ---------
setLaunchDetails(Lang.queryJS('landing.dlAsync.pleaseWait')) setLaunchDetails(Lang.queryJS('landing.dlAsync.pleaseWait'))