mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-11-02 22:16:58 +08:00
69 lines
2.9 KiB
JavaScript
69 lines
2.9 KiB
JavaScript
const fs = require('node:fs')
|
|
const os = require('node:os')
|
|
const path = require('node:path')
|
|
const { execFileSync } = require('node:child_process')
|
|
const { readZipEntries, createZipBuffer, ZIP_STORED } = require('./trim-playwright-driver.cjs')
|
|
|
|
function isMachO(data) {
|
|
if (data.length < 8) return false
|
|
const magic = data.readUInt32BE(0)
|
|
if ([0xfeedface, 0xcefaedfe, 0xfeedfacf, 0xcffaedfe].includes(magic)) return true
|
|
// Java class files share CAFEBABE with fat Mach-O, but contain a class version here.
|
|
if ([0xcafebabe, 0xcafebabf].includes(magic)) return data.readUInt32BE(4) > 0 && data.readUInt32BE(4) < 32
|
|
if ([0xbebafeca, 0xbfbafeca].includes(magic)) return data.readUInt32LE(4) > 0 && data.readUInt32LE(4) < 32
|
|
return false
|
|
}
|
|
|
|
async function rewriteArchive(buffer, signBinary, label = 'app.jar') {
|
|
let count = 0
|
|
const entries = readZipEntries(buffer)
|
|
for (const entry of entries) {
|
|
if (isMachO(entry.data)) {
|
|
entry.data = await signBinary(entry.data, `${label}/${entry.name}`)
|
|
count++
|
|
} else if (entry.name.endsWith('.jar')) {
|
|
const nested = await rewriteArchive(entry.data, signBinary, `${label}/${entry.name}`)
|
|
if (nested.count) {
|
|
entry.data = nested.buffer
|
|
count += nested.count
|
|
// Spring Boot requires nested dependencies to be stored without compression.
|
|
if (entry.name.startsWith('BOOT-INF/lib/')) entry.method = ZIP_STORED
|
|
}
|
|
}
|
|
}
|
|
if (!count) return { buffer, count }
|
|
// Upstream JAR signatures become invalid when their native resources change.
|
|
const unsigned = entries.filter(entry => !/^META-INF\/[^/]+\.(SF|RSA|DSA|EC)$/i.test(entry.name))
|
|
return { buffer: createZipBuffer(unsigned), count }
|
|
}
|
|
|
|
async function signAppJar(appJar, options) {
|
|
const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'mateclaw-jar-sign-'))
|
|
const output = `${appJar}.signed-tmp`
|
|
try {
|
|
const result = await rewriteArchive(fs.readFileSync(appJar), async (data, label) => {
|
|
// Never extract archive-controlled paths onto the filesystem.
|
|
const binary = path.join(temp, 'native')
|
|
fs.writeFileSync(binary, data, { mode: 0o755 })
|
|
const args = ['--force', '--sign', options.identity, '--timestamp', '--options', 'runtime']
|
|
if (options.keychain) args.push('--keychain', options.keychain)
|
|
if (options.entitlements) args.push('--entitlements', options.entitlements)
|
|
args.push(binary)
|
|
console.log(`[sign-jar-natives] ${label}`)
|
|
execFileSync('/usr/bin/codesign', args, { stdio: 'pipe' })
|
|
execFileSync('/usr/bin/codesign', ['--verify', '--strict', binary], { stdio: 'pipe' })
|
|
return fs.readFileSync(binary)
|
|
})
|
|
if (result.count) {
|
|
fs.writeFileSync(output, result.buffer)
|
|
fs.renameSync(output, appJar)
|
|
}
|
|
return result.count
|
|
} finally {
|
|
fs.rmSync(temp, { recursive: true, force: true })
|
|
fs.rmSync(output, { force: true })
|
|
}
|
|
}
|
|
|
|
module.exports = { isMachO, rewriteArchive, signAppJar }
|