Comparing version 0.4.4 to 1.0.0
#!/usr/bin/env node | ||
var path = require("path") | ||
, fs = require("fs") | ||
, child = require("child_process") | ||
var path = require("path"); | ||
var fs = require("fs"); | ||
var rsa = require('node-rsa'); | ||
, program = require("commander") | ||
, ChromeExtension = require("..") | ||
var program = require("commander"); | ||
var ChromeExtension = require(".."); | ||
var pkg = require('../package.json'); | ||
, resolve = path.resolve | ||
, join = path.join | ||
, spawn = child.spawn | ||
, exec = child.exec | ||
var resolve = path.resolve; | ||
var join = path.join; | ||
, cwd = process.cwd() | ||
var cwd = process.cwd(); | ||
program | ||
.version("0.2.8") | ||
.version(pkg.version) | ||
.option("-f, --file [file]", "input/output <file> instead of stdin/stdout") | ||
@@ -28,3 +27,3 @@ .option("-p, --private-key <file>", "relative path to private key [key.pem]") | ||
.description("generate a private key in [directory]/key.pem") | ||
.action(keygen) | ||
.action(keygen); | ||
@@ -34,3 +33,3 @@ program | ||
.description("pack [directory] into a .crx extension") | ||
.action(pack) | ||
.action(pack); | ||
@@ -40,23 +39,24 @@ // program | ||
// .description("unpack a .crx extension into a directory") | ||
// .action(unpack) | ||
// .action(unpack); | ||
program.parse(process.argv) | ||
program.parse(process.argv); | ||
function keygen(dir, cb) { | ||
dir = resolve(cwd, dir) | ||
dir = resolve(cwd, dir); | ||
var key = join(dir, "key.pem") | ||
var keyPath = join(dir, "key.pem"); | ||
fs.exists(key, function(exists) { | ||
if (exists) return cb && typeof(cb) == "function" && cb() | ||
fs.exists(keyPath, function(exists) { | ||
if (exists) { | ||
return cb && typeof(cb) == "function" && cb(); | ||
} | ||
var pubPath = key + ".pub" | ||
, command = "ssh-keygen -N '' -b 1024 -t rsa -f key.pem" | ||
var key = new rsa({ b: 1024 }); | ||
exec(command, {cwd: dir}, function(err) { | ||
if (err) throw err | ||
fs.writeFile(keyPath, key.getPrivatePEM(), function(err){ | ||
if (err){ | ||
throw err; | ||
} | ||
// TODO: find a way to prevent .pub output | ||
fs.unlink(pubPath) | ||
cb && typeof(cb) == "function" && cb() | ||
cb && typeof(cb) == "function" && cb(); | ||
}) | ||
@@ -67,33 +67,26 @@ }) | ||
function pack(dir) { | ||
var input = resolve(cwd, dir) | ||
, output = | ||
program.file === true ? input + ".crx" : | ||
program.file ? resolve(cwd, program.file) : false | ||
var input = resolve(cwd, dir); | ||
var output = program.file === true ? input + ".crx" : (program.file ? resolve(cwd, program.file) : false); | ||
, stream = output ? fs.createWriteStream(output) : process.stdout | ||
, key = program.privateKey | ||
? resolve(cwd, program.privateKey) | ||
: join(input, "key.pem") | ||
var stream = output ? fs.createWriteStream(output) : process.stdout; | ||
var key = program.privateKey ? resolve(cwd, program.privateKey) : join(input, "key.pem"); | ||
, crx = new ChromeExtension({ | ||
rootDirectory: input, | ||
maxBuffer: program.maxBuffer | ||
}) | ||
var crx = new ChromeExtension({ | ||
rootDirectory: input, | ||
maxBuffer: program.maxBuffer | ||
}); | ||
fs.readFile(key, function(err, data) { | ||
if (err) keygen(dir, pack.bind(null, dir)) | ||
if (err) { | ||
throw err; | ||
} | ||
crx.privateKey = data | ||
crx.privateKey = data; | ||
crx.load(function(err) { | ||
if (err) throw err | ||
crx.pack().then(function(crxBuffer) { | ||
stream.end(crxBuffer); | ||
this.pack(function(err, data){ | ||
if (err) throw err | ||
stream.end(data) | ||
this.destroy() | ||
}) | ||
}) | ||
}) | ||
return crx.destroy(); | ||
}); | ||
}); | ||
} |
@@ -1,3 +0,3 @@ | ||
Copyright (c) 2011 Jed Schmidt, http://jed.is/ | ||
Copyright (c) 2014 Jed Schmidt, Thomas Parisot | ||
Permission is hereby granted, free of charge, to any person obtaining | ||
@@ -10,6 +10,6 @@ a copy of this software and associated documentation files (the | ||
the following conditions: | ||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
@@ -16,0 +16,0 @@ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
@@ -5,4 +5,4 @@ { | ||
"description": "Build Google Chrome extensions with node.js", | ||
"version": "0.4.4", | ||
"homepage": "https://github.com/jed/crx", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/oncletom/crx", | ||
"bin": { | ||
@@ -13,7 +13,8 @@ "crx": "./bin/crx.js" | ||
"type": "git", | ||
"url": "git://github.com/jed/crx.git" | ||
"url": "git://github.com/oncletom/crx.git" | ||
}, | ||
"main": "./src/crx.js", | ||
"engines": { | ||
"node": ">=0.7.12" | ||
"node": ">=0.7.12", | ||
"npm": ">=1.4.6" | ||
}, | ||
@@ -24,7 +25,12 @@ "scripts": { | ||
"dependencies": { | ||
"commander": "", | ||
"archiver": "0.8.x", | ||
"wrench": "1.5.x" | ||
"archiver": "^0.8.0", | ||
"commander": "^2.5.0", | ||
"es6-promise": "^2.0.0", | ||
"node-rsa": "^0.1.54", | ||
"rimraf": "^2.2.8", | ||
"wrench": "^1.5.0" | ||
}, | ||
"devDependencies": {} | ||
"devDependencies": { | ||
"tape": "^3.0.3" | ||
} | ||
} |
110
README.md
@@ -1,8 +0,5 @@ | ||
crx | ||
=== | ||
# crx [![Build Status](https://secure.travis-ci.org/oncletom/crx.svg)](http://travis-ci.org/oncletom/crx) | ||
[![Build Status](https://secure.travis-ci.org/jed/crx.png)](http://travis-ci.org/jed/crx) | ||
crx is a [node.js](http://nodejs.org/) command line app for packing Google Chrome extensions. If you'd like to integrate it into your [grunt](http://gruntjs.com/) workflow, give [grunt-crx](https://github.com/oncletom/grunt-crx) a spin. | ||
crx is a [node.js](http://nodejs.org/) command line app for packing Google Chrome extensions. If you'd like to integrate it into your [grunt](http://gruntjs.com/) workflow, give [oncletom](https://github.com/oncletom)'s [grunt-crx](https://github.com/oncletom/grunt-crx) a spin. | ||
## Requirements | ||
@@ -12,3 +9,2 @@ | ||
* openssl | ||
* ssh-keygen | ||
@@ -21,2 +17,4 @@ ## Install | ||
Asynchronous functions returns an [ES6 Promise](https://github.com/jakearchibald/es6-promise). | ||
### ChromeExtension = require("crx") | ||
@@ -27,14 +25,38 @@ ### crx = new ChromeExtension(attrs) | ||
### crx.load(path, callback) | ||
### crx.load(path) | ||
Loads the Chrome Extension from the specified path. | ||
Asynchronously loads the Chrome Extension from the specified path (or uses the `attr.rootDirectory` value). | ||
### crx.pack(callback) | ||
```js | ||
crx.load().then(function(crx){ | ||
// ... | ||
}); | ||
``` | ||
Packs the Chrome Extension, and calls back with a Buffer containing the `.crx` file. | ||
### crx.pack() | ||
Packs the Chrome Extension and resolves the promise with a Buffer containing the `.crx` file. | ||
```js | ||
crx.pack().then(function(crxBuffer){ | ||
fs.writeFile('/tmp/foobar.crx', crxBuffer); | ||
}); | ||
``` | ||
### crx.generateUpdateXML() | ||
Returns a Buffer containing the update.xml file used for autoupdate, as specified for `update_url` in the manifest. In this case, the instance must have a property called `codebase`. | ||
Returns a Buffer containing the update.xml file used for `autoupdate`, as specified for `update_url` in the manifest. In this case, the instance must have a property called `codebase`. | ||
```js | ||
var crx = new ChromeExtension({ ..., codebase: 'https://autoupdateserver.com/myFirstExtension.crx' }); | ||
crx.pack().then(function(crxBuffer){ | ||
// ... | ||
var xmlBuffer = crx.generateUpdateXML(); | ||
fs.writeFile('/foo/bar/update.xml', xmlBuffer); | ||
}); | ||
``` | ||
### crx.destroy() | ||
@@ -44,28 +66,34 @@ | ||
```js | ||
crx.pack().then(function(crxBuffer){ | ||
// ... | ||
return crx.destroy(); | ||
}).then(function(){ | ||
console.log('Extension saved and workspace cleaned up!'); | ||
}); | ||
``` | ||
## Module example | ||
```javascript | ||
var fs = require("fs") | ||
, ChromeExtension = require("crx") | ||
, join = require("path").join | ||
, crx = new ChromeExtension( | ||
codebase: "http://localhost:8000/myFirstExtension.crx", | ||
privateKey: fs.readFileSync(join(__dirname, "key.pem")), | ||
rootDirectory: join(__dirname, "myFirstExtension") | ||
}) | ||
var fs = require("fs"), | ||
var ChromeExtension = require("crx"), | ||
var join = require("path").join, | ||
var crx = new ChromeExtension( | ||
codebase: "http://localhost:8000/myFirstExtension.crx", | ||
privateKey: fs.readFileSync(join(__dirname, "key.pem")) | ||
}); | ||
crx.load(function(err) { | ||
if (err) throw err | ||
crx.load(join(__dirname, "myFirstExtension")) | ||
.then(function() { | ||
return crx.pack(function(crxBuffer){ | ||
var updateXML = crx.generateUpdateXML() | ||
this.pack(function(err, data){ | ||
if (err) throw err | ||
fs.writeFile(join(__dirname, "update.xml"), updateXML) | ||
fs.writeFile(join(__dirname, "myFirstExtension.crx"), crxBuffer) | ||
var updateXML = this.generateUpdateXML() | ||
fs.writeFile(join(__dirname, "update.xml"), updateXML) | ||
fs.writeFile(join(__dirname, "myFirstExtension.crx"), data) | ||
this.destroy() | ||
return crx.destroy(); | ||
}) | ||
}) | ||
}) | ||
``` | ||
@@ -140,4 +168,22 @@ | ||
Copyright (c) 2012 Jed Schmidt. See LICENSE.txt for details. | ||
Copyright (c) 2014 Jed Schmidt, Thomas Parisot | ||
Send any questions or comments [here](http://twitter.com/jedschmidt). | ||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
410
src/crx.js
@@ -1,169 +0,287 @@ | ||
var fs = require("fs") | ||
, path = require("path") | ||
, join = path.join | ||
, sep = require("path").sep | ||
, crypto = require("crypto") | ||
, child = require("child_process") | ||
, wrench = require("wrench") | ||
, archiver = require("archiver") | ||
, spawn = child.spawn | ||
, exec = child.exec | ||
'use strict'; | ||
module.exports = new function() { | ||
function ChromeExtension(attrs) { | ||
if (this instanceof ChromeExtension) { | ||
for (var name in attrs) this[name] = attrs[name] | ||
var fs = require("fs"); | ||
var path = require("path"); | ||
var join = path.join; | ||
var crypto = require("crypto"); | ||
var spawn = require("child_process").spawn; | ||
var wrench = require("wrench"); | ||
var archiver = require("archiver"); | ||
var rm = require('rimraf'); | ||
var Promise = require('es6-promise').Promise; | ||
this.path = join("tmp", "crx-" + (Math.random() * 1e17).toString(36)) | ||
} | ||
else return new ChromeExtension(attrs) | ||
function ChromeExtension(attrs) { | ||
if ((this instanceof ChromeExtension) !== true) { | ||
return new ChromeExtension(attrs); | ||
} | ||
ChromeExtension.prototype = this | ||
/* | ||
Defaults | ||
*/ | ||
this.appId = null; | ||
this.destroy = function() { | ||
wrench.rmdirSyncRecursive(path.dirname(this.path)) | ||
this.manifest = ''; | ||
this.loaded = false; | ||
this.rootDirectory = ''; | ||
this.publicKey = null; | ||
this.privateKey = null; | ||
this.codebase = null; | ||
/* | ||
Copying attributes | ||
*/ | ||
for (var name in attrs) { | ||
this[name] = attrs[name]; | ||
} | ||
this.pack = function(cb) { | ||
if (!this.loaded) return this.load(function(err) { | ||
return err ? cb(err) : this.pack(cb) | ||
}) | ||
this.path = join("tmp", "crx-" + (Math.random() * 1e17).toString(36)) | ||
} | ||
this.generatePublicKey(function(err) { | ||
if (err) return cb(err) | ||
ChromeExtension.prototype = { | ||
var manifest = JSON.stringify(this.manifest) | ||
/** | ||
* Destroys generated files. | ||
* | ||
* @returns {Promise} | ||
*/ | ||
destroy: function () { | ||
var path = this.path; | ||
this.writeFile("manifest.json", manifest, function(err) { | ||
if (err) return cb(err) | ||
this.loadContents(function(err) { | ||
if (err) return cb(err) | ||
return new Promise(function(resolve, reject){ | ||
rm(path, function(err){ | ||
if (err){ | ||
return reject(err); | ||
} | ||
this.generateSignature() | ||
this.generatePackage() | ||
resolve(); | ||
}); | ||
}); | ||
}, | ||
cb.call(this, null, this.package) | ||
/** | ||
* Packs the content of the extension in a crx file. | ||
* | ||
* @returns {Promise} | ||
* @example | ||
* | ||
* crx.pack().then(function(crxContent){ | ||
* // do something with the crxContent binary data | ||
* }); | ||
* | ||
*/ | ||
pack: function () { | ||
if (!this.loaded) { | ||
return this.load().then(this.pack.bind(this)); | ||
} | ||
var selfie = this; | ||
return selfie.writeFile("manifest.json", JSON.stringify(selfie.manifest)) | ||
.then(this.generatePublicKey.bind(this)) | ||
.then(function(publicKey){ | ||
selfie.publicKey = publicKey; | ||
return selfie.loadContents().then(function (contents) { | ||
var signature = selfie.generateSignature(contents); | ||
return selfie.generatePackage(signature, publicKey, contents); | ||
}) | ||
}) | ||
}) | ||
} | ||
}); | ||
}, | ||
this.load = function(cb) { | ||
/** | ||
* Loads extension manifest and copies its content to a workable path. | ||
* | ||
* @param {string=} path | ||
* @returns {Promise} | ||
*/ | ||
load: function (path) { | ||
if (!fs.existsSync("tmp")) { | ||
fs.mkdirSync("tmp") | ||
fs.mkdirSync("tmp"); | ||
} | ||
wrench.copyDirRecursive(this.rootDirectory, this.path, function(err) { | ||
if (err) { throw err } | ||
this.manifest = require(join(process.cwd(), this.path, "manifest.json")) | ||
this.loaded = true | ||
cb.call(this) | ||
}.bind(this)) | ||
} | ||
var selfie = this; | ||
this.readFile = function(name, cb) { | ||
var path = join(this.path, name) | ||
return new Promise(function(resolve, reject){ | ||
wrench.copyDirRecursive(path || selfie.rootDirectory, selfie.path, function (err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
fs.readFile(path, "binary", function(err, data) { | ||
if (err) return cb.call(this, err) | ||
selfie.manifest = require(join(process.cwd(), selfie.path, "manifest.json")); | ||
selfie.loaded = true; | ||
cb.call(this, null, this[name] = data) | ||
}.bind(this)) | ||
} | ||
resolve(selfie); | ||
}); | ||
}); | ||
}, | ||
this.writeFile = function(path, data, cb) { | ||
path = join(this.path, path) | ||
/** | ||
* Writes data into the extension workable directory. | ||
* | ||
* @param {string} path | ||
* @param {*} data | ||
* @returns {Promise} | ||
*/ | ||
writeFile: function (path, data) { | ||
var absPath = join(this.path, path); | ||
fs.writeFile(path, data, function(err, data) { | ||
if (err) return cb.call(this, err) | ||
return new Promise(function(resolve, reject){ | ||
fs.writeFile(absPath, data, function (err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
cb.call(this) | ||
}.bind(this)) | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
}, | ||
this.generatePublicKey = function(cb) { | ||
var rsa = spawn("openssl", ["rsa", "-pubout", "-outform", "DER"]) | ||
/** | ||
* Generates a public key. | ||
* | ||
* BC BREAK `this.publicKey` is not stored anymore (since 1.0.0) | ||
* | ||
* @returns {Promise} | ||
* @example | ||
* | ||
* crx.generatePublicKey(function(publicKey){ | ||
* // do something with publicKey | ||
* }); | ||
*/ | ||
generatePublicKey: function () { | ||
var privateKey = this.privateKey; | ||
rsa.stdout.on("data", function(data) { | ||
this.publicKey = data | ||
cb && cb.call(this, null, this) | ||
}.bind(this)) | ||
return new Promise(function(resolve, reject){ | ||
var rsa = spawn("openssl", ["rsa", "-pubout", "-outform", "DER"]); | ||
rsa.stdin.end(this.privateKey) | ||
} | ||
rsa.stdout.on("data", function (publicKey) { | ||
resolve(publicKey); | ||
}); | ||
this.generateSignature = function() { | ||
return this.signature = new Buffer( | ||
rsa.on('error', reject); | ||
rsa.stdin.end(privateKey); | ||
}); | ||
}, | ||
/** | ||
* Generates a SHA1 package signature. | ||
* | ||
* BC BREAK `this.signature` is not stored anymore (since 1.0.0) | ||
* | ||
* @param {Buffer} contents | ||
* @returns {Buffer} | ||
*/ | ||
generateSignature: function (contents) { | ||
return new Buffer( | ||
crypto | ||
.createSign("sha1") | ||
.update(this.contents) | ||
.update(contents) | ||
.sign(this.privateKey), | ||
"binary" | ||
) | ||
} | ||
); | ||
}, | ||
this.loadContents = function(cb) { | ||
var archive = archiver("zip") | ||
this.contents = "" | ||
/** | ||
* | ||
* BC BREAK `this.contents` is not stored anymore (since 1.0.0) | ||
* | ||
* @returns {Promise} | ||
*/ | ||
loadContents: function () { | ||
var archive = archiver("zip"); | ||
var selfie = this; | ||
files = wrench.readdirSyncRecursive(this.path) | ||
for (var i = 0; i < files.length; i++) { | ||
current = files[i] | ||
stat = fs.statSync(join(this.path, current)) | ||
if (stat.isFile() && current !== "key.pem") { | ||
archive.append(fs.createReadStream(join(this.path, current)), { name: current }) | ||
} | ||
} | ||
return new Promise(function(resolve, reject){ | ||
var contents = new Buffer(''); | ||
var allFiles = []; | ||
archive.finalize() | ||
// Relates to the issue: "Event 'finished' no longer valid #18" | ||
// https://github.com/jed/crx/issues/18 | ||
// TODO: Buffer concat could be a problem when building a big extension. | ||
// So ideally only the 'finish' callback must be used. | ||
archive.on('readable', function() { | ||
this.contents = !this.contents.length ? archive.read() : Buffer.concat([this.contents, archive.read()]) | ||
}.bind(this)) | ||
// the callback is called many times | ||
// when 'files' is null, it means we accumulated everything | ||
// hence this weird setup | ||
wrench.readdirRecursive(selfie.path, function(err, files){ | ||
if (err){ | ||
return reject(err); | ||
} | ||
archive.on('finish', function() { | ||
cb.call(this) | ||
}.bind(this)) | ||
// stack unless 'files' is null | ||
if (files){ | ||
allFiles = allFiles.concat(files); | ||
return; | ||
} | ||
archive.on("error", function(err) { | ||
throw err | ||
}) | ||
} | ||
this.generatePackage = function() { | ||
var signature = this.signature | ||
, publicKey = this.publicKey | ||
, contents = this.contents | ||
allFiles.forEach(function (file) { | ||
var filePath = join(selfie.path, file); | ||
var stat = fs.statSync(filePath); | ||
, keyLength = publicKey.length | ||
, sigLength = signature.length | ||
, zipLength = contents.length | ||
, length = 16 + keyLength + sigLength + zipLength | ||
if (stat.isFile() && file !== "key.pem") { | ||
archive.append(fs.createReadStream(filePath), { name: file }); | ||
} | ||
}); | ||
, crx = new Buffer(length) | ||
archive.finalize(); | ||
crx.write("Cr24" + Array(13).join("\x00"), "binary") | ||
// Relates to the issue: "Event 'finished' no longer valid #18" | ||
// https://github.com/jed/crx/issues/18 | ||
// TODO: Buffer concat could be a problem when building a big extension. | ||
// So ideally only the 'finish' callback must be used. | ||
archive.on('readable', function () { | ||
contents = Buffer.concat([contents, archive.read()]); | ||
}); | ||
crx[4] = 2 | ||
crx.writeUInt32LE(keyLength, 8) | ||
crx.writeUInt32LE(sigLength, 12) | ||
archive.on('finish', function () { | ||
resolve(contents); | ||
}); | ||
publicKey.copy(crx, 16) | ||
signature.copy(crx, 16 + keyLength) | ||
contents.copy(crx, 16 + keyLength + sigLength) | ||
archive.on("error", reject); | ||
}); | ||
}); | ||
}, | ||
return this.package = crx | ||
} | ||
/** | ||
* Generates and returns a signed package from extension content. | ||
* | ||
* BC BREAK `this.package` is not stored anymore (since 1.0.0) | ||
* | ||
* @param {Buffer} signature | ||
* @param {Buffer} publicKey | ||
* @param {Buffer} contents | ||
* @returns {Buffer} | ||
*/ | ||
generatePackage: function (signature, publicKey, contents) { | ||
var keyLength = publicKey.length; | ||
var sigLength = signature.length; | ||
var zipLength = contents.length; | ||
var length = 16 + keyLength + sigLength + zipLength; | ||
this.generateAppId = function() { | ||
return this.appId = crypto | ||
var crx = new Buffer(length); | ||
crx.write("Cr24" + Array(13).join("\x00"), "binary"); | ||
crx[4] = 2; | ||
crx.writeUInt32LE(keyLength, 8); | ||
crx.writeUInt32LE(sigLength, 12); | ||
publicKey.copy(crx, 16); | ||
signature.copy(crx, 16 + keyLength); | ||
contents.copy(crx, 16 + keyLength + sigLength); | ||
return crx; | ||
}, | ||
/** | ||
* Generates an appId from the publicKey. | ||
* | ||
* BC BREAK `this.appId` is not stored anymore (since 1.0.0) | ||
* | ||
* @returns {string} | ||
*/ | ||
generateAppId: function () { | ||
return crypto | ||
.createHash("sha256") | ||
@@ -173,22 +291,30 @@ .update(this.publicKey) | ||
.slice(0, 32) | ||
.replace(/./g, function(x) { | ||
return (parseInt(x, 16) + 10).toString(26) | ||
}) | ||
} | ||
.replace(/./g, function (x) { | ||
return (parseInt(x, 16) + 10).toString(26); | ||
}); | ||
}, | ||
this.generateUpdateXML = function() { | ||
if (!this.codebase) throw new Error("No URL provided for update.xml.") | ||
/** | ||
* Generates an updateXML file from the extension content. | ||
* | ||
* BC BREAK `this.updateXML` is not stored anymore (since 1.0.0) | ||
* | ||
* @returns {Buffer} | ||
*/ | ||
generateUpdateXML: function () { | ||
if (!this.codebase) { | ||
throw new Error("No URL provided for update.xml."); | ||
} | ||
return this.updateXML = | ||
Buffer( | ||
"<?xml version='1.0' encoding='UTF-8'?>\n" + | ||
"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>\n" + | ||
" <app appid='" + this.generateAppId() + "'>\n" + | ||
" <updatecheck codebase='" + this.codebase + "' version='" + this.manifest.version + "' />\n" + | ||
" </app>\n" + | ||
"</gupdate>" | ||
) | ||
return Buffer( | ||
"<?xml version='1.0' encoding='UTF-8'?>\n" + | ||
"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>\n" + | ||
" <app appid='" + (this.appId || this.generateAppId()) + "'>\n" + | ||
" <updatecheck codebase='" + this.codebase + "' version='" + this.manifest.version + "' />\n" + | ||
" </app>\n" + | ||
"</gupdate>" | ||
); | ||
} | ||
}; | ||
return ChromeExtension | ||
} | ||
module.exports = ChromeExtension; |
@@ -1,20 +0,29 @@ | ||
var fs = require("fs") | ||
, assert = require("assert") | ||
, ChromeExtension = require("../") | ||
, join = require("path").join | ||
, crx = new ChromeExtension({ | ||
privateKey: fs.readFileSync(join(__dirname, "key.pem")), | ||
codebase: "http://localhost:8000/myFirstExtension.crx", | ||
rootDirectory: join(__dirname, "myFirstExtension") | ||
}) | ||
var fs = require("fs"); | ||
var test = require("tape"); | ||
var ChromeExtension = require("../"); | ||
var join = require("path").join; | ||
var crx = new ChromeExtension({ | ||
privateKey: fs.readFileSync(join(__dirname, "key.pem")), | ||
codebase: "http://localhost:8000/myFirstExtension.crx", | ||
rootDirectory: join(__dirname, "myFirstExtension") | ||
}); | ||
crx.pack(function(err, data){ | ||
if (err) throw err | ||
test('it should pack the test extension', function(t){ | ||
t.plan(3); | ||
var updateXML = this.generateUpdateXML() | ||
crx.pack() | ||
.then(function(packageData){ | ||
t.ok(packageData instanceof Buffer); | ||
fs.writeFile(join(__dirname, "update.xml"), updateXML) | ||
fs.writeFile(join(__dirname, "myFirstExtension.crx"), data) | ||
var updateXML = crx.generateUpdateXML(); | ||
this.destroy() | ||
}) | ||
t.ok(updateXML instanceof Buffer); | ||
fs.writeFile(join(__dirname, "update.xml"), updateXML); | ||
fs.writeFile(join(__dirname, "myFirstExtension.crx"), packageData); | ||
return crx.destroy(); | ||
}) | ||
.then(t.pass.bind(t)) | ||
.catch(t.error.bind(t)); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
22956
364
0
0
185
6
1
3
+ Addedes6-promise@^2.0.0
+ Addednode-rsa@^0.1.54
+ Addedrimraf@^2.2.8
+ Addedasn1@0.2.6(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedcommander@2.20.3(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedes6-promise@2.3.0(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addednode-rsa@0.1.54(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedrimraf@2.7.1(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removedcommander@12.1.0(transitive)
Updatedarchiver@^0.8.0
Updatedcommander@^2.5.0
Updatedwrench@^1.5.0