112 lines
No EOL
3.5 KiB
JavaScript
112 lines
No EOL
3.5 KiB
JavaScript
/**
|
|
* CRX Extractor - Utility for handling Chrome extension files
|
|
* This module provides functions to extract and install Chrome extensions from CRX files
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const AdmZip = require('adm-zip');
|
|
|
|
/**
|
|
* Extracts a CRX file to the specified directory
|
|
*
|
|
* @param {string} crxFilePath - Path to the CRX file
|
|
* @param {string} outputDir - Directory to extract the CRX file to
|
|
* @returns {Promise<Object>} Result of the extraction
|
|
*/
|
|
async function extractCrxFile(crxFilePath, outputDir) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
// Read the CRX file
|
|
const crxData = fs.readFileSync(crxFilePath);
|
|
|
|
// Check CRX header (Chrome extensions start with "Cr24")
|
|
if (crxData.slice(0, 4).toString() !== 'Cr24') {
|
|
return reject(new Error('Invalid CRX file format'));
|
|
}
|
|
|
|
// CRX format:
|
|
// [0-3] "Cr24" (Chrome extension signature)
|
|
// [4-7] Version number (uint32)
|
|
// [8-11] Public key length (uint32)
|
|
// [12-15] Signature length (uint32)
|
|
|
|
// Extract header information
|
|
const publicKeyLength = crxData.readUInt32LE(8);
|
|
const signatureLength = crxData.readUInt32LE(12);
|
|
|
|
// ZIP data starts after the header, public key, and signature
|
|
const zipStartOffset = 16 + publicKeyLength + signatureLength;
|
|
|
|
// Extract the ZIP portion of the CRX file
|
|
const zipData = crxData.slice(zipStartOffset);
|
|
|
|
// Write the ZIP data to a temporary file
|
|
const tempZipPath = path.join(require('os').tmpdir(), `${path.basename(crxFilePath, '.crx')}.zip`);
|
|
fs.writeFileSync(tempZipPath, zipData);
|
|
|
|
// Extract the ZIP file using AdmZip
|
|
const zip = new AdmZip(tempZipPath);
|
|
zip.extractAllTo(outputDir, true);
|
|
|
|
// Clean up the temporary ZIP file
|
|
fs.unlinkSync(tempZipPath);
|
|
|
|
// Check if manifest.json exists in the extracted files
|
|
const manifestPath = path.join(outputDir, 'manifest.json');
|
|
if (!fs.existsSync(manifestPath)) {
|
|
return reject(new Error('No manifest.json found in the extension'));
|
|
}
|
|
|
|
// Read the manifest.json file to get extension information
|
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
|
|
resolve({
|
|
success: true,
|
|
manifest,
|
|
extensionId: manifest.name ? manifest.name.replace(/[^a-zA-Z0-9]/g, '').toLowerCase() : null
|
|
});
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Installs a Chrome extension from a CRX file
|
|
*
|
|
* @param {string} crxFilePath - Path to the CRX file
|
|
* @param {string} extensionsDir - Directory where extensions are stored
|
|
* @returns {Promise<Object>} Result of the installation
|
|
*/
|
|
async function installCrxExtension(crxFilePath, extensionsDir) {
|
|
try {
|
|
const extensionId = path.basename(crxFilePath, '.crx');
|
|
const extractPath = path.join(extensionsDir, extensionId);
|
|
|
|
// Create directory if it doesn't exist
|
|
if (!fs.existsSync(extractPath)) {
|
|
fs.mkdirSync(extractPath, { recursive: true });
|
|
}
|
|
|
|
// Extract the CRX file
|
|
const extractResult = await extractCrxFile(crxFilePath, extractPath);
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Extension installed successfully',
|
|
extensionId,
|
|
manifest: extractResult.manifest
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
message: `Failed to install extension: ${error.message}`
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
extractCrxFile,
|
|
installCrxExtension
|
|
};
|