@hibi_10000/crx 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/LICENSE.txt +23 -0
- package/README.md +205 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +89 -0
- package/dist/cli.js.map +1 -0
- package/dist/crx2.js +36 -0
- package/dist/crx2.js.map +1 -0
- package/dist/crx3.js +90 -0
- package/dist/crx3.js.map +1 -0
- package/dist/crx3.pb.js +17 -0
- package/dist/crx3.pb.js.map +1 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/resolver.js +41 -0
- package/dist/resolver.js.map +1 -0
- package/package.json +71 -0
- package/src/cli.ts +175 -0
- package/src/crx2.ts +43 -0
- package/src/crx3.pb.d.ts +22 -0
- package/src/crx3.pb.js +37 -0
- package/src/crx3.ts +111 -0
- package/src/index.ts +232 -0
- package/src/resolver.ts +54 -0
- package/tsconfig.json +22 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
### 5.1.0 (2026/03/01 09:55 +09:00)
|
|
4
|
+
- Migrate ESM (still support CommonJS require)
|
|
5
|
+
- Use Typescript with tsdown
|
|
6
|
+
- Drop support for Node.js v10 to v17 and v19
|
|
7
|
+
- Replace node-rsa with node:crypto module
|
|
8
|
+
- Update dependencies
|
|
9
|
+
|
|
10
|
+
## Original Changelog from Forked
|
|
11
|
+
|
|
12
|
+
### 5.0.1 (2019/07/22 09:57 +00:00)
|
|
13
|
+
- [#107](https://github.com/oncletom/crx/pull/107) fix: loading relative path (@ahwayakchih)
|
|
14
|
+
|
|
15
|
+
### v5.0.0 (2019/04/17 08:00 +00:00)
|
|
16
|
+
- [#106](https://github.com/oncletom/crx/pull/106) chore: update CHANGELOG (@ahwayakchih)
|
|
17
|
+
- [#105](https://github.com/oncletom/crx/pull/105) Update dependencies and prepare for new release (v5.0.0) (@ahwayakchih)
|
|
18
|
+
- [#104](https://github.com/oncletom/crx/pull/104) fix: create private key file outside extension directory by default (@ahwayakchih)
|
|
19
|
+
- [#103](https://github.com/oncletom/crx/pull/103) Move CLI script to src folder (@arkon)
|
|
20
|
+
- [#100](https://github.com/oncletom/crx/pull/100) Move CRX2 logic to separate file (@arkon)
|
|
21
|
+
- [#102](https://github.com/oncletom/crx/pull/102) feat: use `manifest.minimum_chrome_version` as XML's `prodversionmin` (@ahwayakchih)
|
|
22
|
+
- [#99](https://github.com/oncletom/crx/pull/99) docs: add `--crx-format` to README (@ahwayakchih)
|
|
23
|
+
- [#98](https://github.com/oncletom/crx/pull/98) feat: add support for CRXv3 (@ahwayakchih)
|
|
24
|
+
- [#97](https://github.com/oncletom/crx/pull/97) chore: update dependencies (#97) (@ahwayakchih)
|
|
25
|
+
- [#78](https://github.com/oncletom/crx/pull/78) Add generateAppId sample (#78) (@NN---)
|
|
26
|
+
|
|
27
|
+
### v4.0.1 (2019/02/03 16:17 +00:00)
|
|
28
|
+
- [#96](https://github.com/oncletom/crx/pull/96) Remove deprecated crx.writeFile() (@oncletom)
|
|
29
|
+
|
|
30
|
+
### v4.0.0 (2019/02/03 15:57 +00:00)
|
|
31
|
+
- [#95](https://github.com/oncletom/crx/pull/95) Release crx@4 (@oncletom)
|
|
32
|
+
- [#93](https://github.com/oncletom/crx/pull/93) fix demo code (@g8up)
|
|
33
|
+
- [#88](https://github.com/oncletom/crx/pull/88) Bump Node.js version requirement (@oncletom)
|
|
34
|
+
- [#90](https://github.com/oncletom/crx/pull/90) Fix syntax in module usage (@blimmer)
|
|
35
|
+
- [#83](https://github.com/oncletom/crx/pull/83) Update dependencies (@oncletom)
|
|
36
|
+
- [#81](https://github.com/oncletom/crx/pull/81) Fix extension ID calculation from path (@oncletom, @conioh)
|
|
37
|
+
- [#76](https://github.com/oncletom/crx/pull/76) Add Appveyor configuration to test build on Windows (@oncletom)
|
|
38
|
+
- [#71](https://github.com/oncletom/crx/pull/71) Remove the manifest data from cache on crx.load() (@binhqx)
|
|
39
|
+
- [#75](https://github.com/oncletom/crx/pull/75) [Snyk Update] New fixes for 1 vulnerable dependency path (@snyk-bot)
|
|
40
|
+
|
|
41
|
+
### v3.2.1 (2016/10/13 13:13 +00:00)
|
|
42
|
+
- [#67](https://github.com/oncletom/crx/pull/67) Drop iojs from package.engines (@dsblv)
|
|
43
|
+
|
|
44
|
+
### v3.2.0 (2016/09/22 23:25 +00:00)
|
|
45
|
+
- [#66](https://github.com/oncletom/crx/pull/66) Add the ability to load a list of files in addition to a path. (@oncletom)
|
|
46
|
+
|
|
47
|
+
### v3.1.0 (2016/09/22 14:06 +00:00)
|
|
48
|
+
- [#62](https://github.com/oncletom/crx/pull/62) Remove intermediate copy and use of temporary files (@oncletom)
|
|
49
|
+
- [#63](https://github.com/oncletom/crx/pull/63) Add code coverage and add additional tests (@oncletom)
|
|
50
|
+
|
|
51
|
+
### v3.0.4 (2016/09/21 13:00 +00:00)
|
|
52
|
+
- [#58](https://github.com/oncletom/crx/pull/58) [security fix] Updated archiver dependency; drops support for node 0.8.x (@PavelVanecek)
|
|
53
|
+
- [#56](https://github.com/oncletom/crx/pull/56) Update CLI documentation with -o instead of deprecated -f (@nhoizey)
|
|
54
|
+
- [#52](https://github.com/oncletom/crx/pull/52) Generate 2048-bit keys at the keygen CLI (@ngyikp)
|
|
55
|
+
- [#50](https://github.com/oncletom/crx/pull/50) test crx on node 4.1 and 5 (@joscha)
|
|
56
|
+
|
|
57
|
+
### v3.0.3 (2015/07/22 16:10 +00:00)
|
|
58
|
+
- [#47](https://github.com/oncletom/crx/pull/47) added .npmignore file (@PavelVanecek)
|
|
59
|
+
|
|
60
|
+
### v3.0.2 (2015/02/06 11:28 +00:00)
|
|
61
|
+
- [#39](https://github.com/oncletom/crx/pull/39) Do not concatenate a possible null buffer. (@oncletom)
|
|
62
|
+
- [#37](https://github.com/oncletom/crx/pull/37) fix pack instruction in Module example (@qbarlas)
|
|
63
|
+
|
|
64
|
+
### v2.0.1 (2015/01/15 18:28 +00:00)
|
|
65
|
+
- [#34](https://github.com/oncletom/crx/pull/34) Fix various issues with CLI and creating .pem and .crx files (@Batterii)
|
|
66
|
+
- [#35](https://github.com/oncletom/crx/pull/35) destroy() is not used (@okuryu)
|
|
67
|
+
- [#26](https://github.com/oncletom/crx/pull/26) fixes minor code problems, such as missing semicolons, etc. (@joscha)
|
|
68
|
+
- [#29](https://github.com/oncletom/crx/pull/29) generatePublicKey promise and generateAppId state (@joscha)
|
|
69
|
+
- [#30](https://github.com/oncletom/crx/pull/30) use temp module (@joscha)
|
|
70
|
+
- [#27](https://github.com/oncletom/crx/pull/27) throw error if public key is not set, yet (@joscha)
|
|
71
|
+
|
|
72
|
+
### v2.0.0 (2014/11/29 16:39 +00:00)
|
|
73
|
+
- [#24](https://github.com/oncletom/crx/pull/24) Unsigned archives (@oncletom)
|
|
74
|
+
|
|
75
|
+
### v1.1.0 (2014/11/29 15:55 +00:00)
|
|
76
|
+
- [#25](https://github.com/oncletom/crx/pull/25) Pure JavaScript public key (@oncletom)
|
|
77
|
+
|
|
78
|
+
### v1.0.0 (2014/11/25 11:45 +00:00)
|
|
79
|
+
- [#22](https://github.com/oncletom/crx/pull/22) Remove system ssh-keygen dependency (@oncletom)
|
|
80
|
+
- [#20](https://github.com/oncletom/crx/pull/20) Promise-based interface (@oncletom)
|
|
81
|
+
- [#19](https://github.com/oncletom/crx/pull/19) Event 'finished' no longer valid #18 (@yuryoparin)
|
|
82
|
+
- [#17](https://github.com/oncletom/crx/pull/17) Fix deleting remaining ./tmp directory (@jokesterfr)
|
|
83
|
+
- [#16](https://github.com/oncletom/crx/pull/16) removed bufferstream dependency (@christian-bromann)
|
|
84
|
+
- [#14](https://github.com/oncletom/crx/pull/14) Support long private keys in latest Chrome 32 (@vitalets)
|
|
85
|
+
- [#10](https://github.com/oncletom/crx/pull/10) Windows Compatibility (@adotout)
|
|
86
|
+
- [#8](https://github.com/oncletom/crx/pull/8) Use proper stdin options in child_process.spawn() instead of passing a file child.stdin.end() (@nkakuev)
|
|
87
|
+
- [#3](https://github.com/oncletom/crx/pull/3) Added support for maxBuffer (@oncletom)
|
|
88
|
+
- [#2](https://github.com/oncletom/crx/pull/2) Updated the README example upon `crx` API (@oncletom)
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hibi_10000
|
|
4
|
+
Copyright (c) 2019 Jed Schmidt, Thomas Parisot and contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
+
a copy of this software and associated documentation files (the
|
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
12
|
+
the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be
|
|
15
|
+
included in all copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# crx [](http://travis-ci.org/oncletom/crx) [](https://ci.appveyor.com/project/oncletom/crx)
|
|
2
|
+
|
|
3
|
+
> crx is a utility to **package Google Chrome extensions** via a *Node API* and the *command line*. It is written **purely in JavaScript** and **does not require OpenSSL**!
|
|
4
|
+
|
|
5
|
+
Packages are available to use `crx` with:
|
|
6
|
+
|
|
7
|
+
- *grunt*: [grunt-crx](https://npmjs.com/grunt-crx)
|
|
8
|
+
- *gulp*: [gulp-crx-pack](https://npmjs.com/gulp-crx-pack)
|
|
9
|
+
- *webpack*: [crx-webpack-plugin](https://npmjs.com/crx-webpack-plugin)
|
|
10
|
+
|
|
11
|
+
**Compatibility**: this extension is compatible with `node>=10`.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
$ npm install crx
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Module API
|
|
20
|
+
|
|
21
|
+
Asynchronous functions returns a native ECMAScript Promise.
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
import fs from 'fs';
|
|
25
|
+
import path from 'path';
|
|
26
|
+
|
|
27
|
+
import ChromeExtension from 'crx';
|
|
28
|
+
|
|
29
|
+
const crx = new ChromeExtension({
|
|
30
|
+
codebase: 'http://localhost:8000/myExtension.crx',
|
|
31
|
+
privateKey: fs.readFileSync('./key.pem')
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
crx.load(path.resolve(import.meta.dirname, './myExtension'))
|
|
35
|
+
.then(crx => crx.pack())
|
|
36
|
+
.then(crxBuffer => {
|
|
37
|
+
const updateXML = crx.generateUpdateXML()
|
|
38
|
+
|
|
39
|
+
fs.writeFileSync('../update.xml', updateXML);
|
|
40
|
+
fs.writeFileSync('../myExtension.crx', crxBuffer);
|
|
41
|
+
})
|
|
42
|
+
.catch(err => {
|
|
43
|
+
console.error(err);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### ChromeExtension = require("crx")
|
|
48
|
+
### crx = new ChromeExtension(attrs?)
|
|
49
|
+
|
|
50
|
+
This module exports the `ChromeExtension` constructor directly, which can take an optional attribute object, which is used to extend the instance.
|
|
51
|
+
|
|
52
|
+
`attrs` is an optional object with advanced configuration elements:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
crx = new ChromeExtension({
|
|
56
|
+
// By default, we exclude bundling crx extensions within extensions
|
|
57
|
+
// You can change that, and ignore other files as well
|
|
58
|
+
ignore: ['*.crx']
|
|
59
|
+
// By default, it produces extensions compatible with CRX version 3 (since Chromium 64)
|
|
60
|
+
// CRX version 2 is for Chromium versions prior to 64
|
|
61
|
+
version: 3
|
|
62
|
+
})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### crx.load(path|files)
|
|
66
|
+
|
|
67
|
+
Prepares the temporary workspace for the Chrome Extension located at `path` — which is expected to directly contain `manifest.json`.
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
crx.load('/path/to/extension').then(crx => {
|
|
71
|
+
// ...
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Alternatively, you can pass a list of files — the first `manifest.json` file to be found will be considered as the root of the application.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
crx.load(['/my/extension/manifest.json', '/my/extension/background.json']).then(crx => {
|
|
79
|
+
// ...
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### crx.pack()
|
|
84
|
+
|
|
85
|
+
Packs the Chrome Extension and resolves the promise with a Buffer containing the `.crx` file.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
crx.load('/path/to/extension')
|
|
89
|
+
.then(crx => crx.pack())
|
|
90
|
+
.then(crxBuffer => {
|
|
91
|
+
fs.writeFileSync('/tmp/foobar.crx', crxBuffer);
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### crx.generateUpdateXML()
|
|
96
|
+
|
|
97
|
+
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`.
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
const crx = new ChromeExtension({ ..., codebase: 'https://autoupdateserver.com/myFirstExtension.crx' });
|
|
101
|
+
|
|
102
|
+
crx.load('/path/to/extension')
|
|
103
|
+
.then(crx => crx.pack())
|
|
104
|
+
.then(crxBuffer => {
|
|
105
|
+
// ...
|
|
106
|
+
const xmlBuffer = crx.generateUpdateXML();
|
|
107
|
+
fs.writeFileSync('/foo/bar/update.xml', xmlBuffer);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### crx.generateAppId
|
|
112
|
+
|
|
113
|
+
Generates application id (extension id) from given path.
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
new crx().generateAppId('/path/to/ext') // epgkjnfaepceeghkjflpimappmlalchn
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## CLI API
|
|
120
|
+
|
|
121
|
+
### crx pack [directory] [--crx-version number] [-o file] [--zip-output file] [-p private-key]
|
|
122
|
+
|
|
123
|
+
Pack the specified directory into a .crx package, and output it to stdout. If no directory is specified, the current working directory is used.
|
|
124
|
+
|
|
125
|
+
Use the `--crx-version` option to specify which CRX format version to output. Can be either "2" or "3", defaults to "3".
|
|
126
|
+
|
|
127
|
+
Use the `-o` option to write the signed extension to a file instead of stdout.
|
|
128
|
+
|
|
129
|
+
Use the `--zip-output` option to write the unsigned extension to a file.
|
|
130
|
+
|
|
131
|
+
Use the `-p` option to specify an external private key. If this is not used, `key.pem` is used from within the directory. If this option is not used and no `key.pem` file exists, one will be generated automatically.
|
|
132
|
+
|
|
133
|
+
Use the `-b` option to specify the maximum buffer allowed to generate extension. By default, will rely on `node` internal setting (~200KB).
|
|
134
|
+
|
|
135
|
+
### crx keygen [directory]
|
|
136
|
+
|
|
137
|
+
Generate a 2048-bit RSA private key within the directory. This is called automatically if a key is not specified, and `key.pem` does not exist.
|
|
138
|
+
|
|
139
|
+
Use the `--force` option to overwrite an existing private key located in the same given folder.
|
|
140
|
+
|
|
141
|
+
### crx --help
|
|
142
|
+
|
|
143
|
+
Show information about using this utility, generated by [commander](https://github.com/visionmedia/commander.js).
|
|
144
|
+
|
|
145
|
+
## CLI example
|
|
146
|
+
|
|
147
|
+
Given the following directory structure:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
└─┬ myFirstExtension
|
|
151
|
+
├── manifest.json
|
|
152
|
+
└── icon.png
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
run this:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
$ cd myFirstExtension
|
|
159
|
+
$ crx pack -o
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
to generate this:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
├─┬ myFirstExtension
|
|
166
|
+
│ ├── manifest.json
|
|
167
|
+
│ ├── icon.png
|
|
168
|
+
│ └── key.pem
|
|
169
|
+
└── myFirstExtension.crx
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
You can also name the output file like this:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
$ cd myFirstExtension
|
|
176
|
+
$ crx pack -o myFirstExtension.crx
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
to get the same results, or also pipe to the file manually like this.
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
$ cd myFirstExtension
|
|
183
|
+
$ crx pack > ../myFirstExtension.crx
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
As you can see a key is generated for you at `key.pem` if none exists. You can also specify an external key. So if you have this:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
├─┬ myFirstExtension
|
|
190
|
+
│ ├── manifest.json
|
|
191
|
+
│ └── icon.png
|
|
192
|
+
└── myPrivateKey.pem
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
you can run this:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
$ crx pack myFirstExtension -p myPrivateKey.pem -o
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
to sign your package without keeping the key in the directory.
|
|
202
|
+
|
|
203
|
+
# License
|
|
204
|
+
|
|
205
|
+
[MIT License](LICENSE).
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import ChromeExtension from "./index.js";
|
|
3
|
+
import { version } from "./package.js";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
import { program } from "commander";
|
|
8
|
+
|
|
9
|
+
//#region src/cli.ts
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
program.version(version);
|
|
12
|
+
program.command("keygen [directory]").option("--force", "overwrite the private key if it exists").option("-c, --crx-version [number]", "CRX format version, can be either 2 or 3, defaults to 3", parseInt).description("generate a private key in [directory]/key.pem").action(keygen);
|
|
13
|
+
program.command("pack [directory]").description("pack [directory] into a .crx extension").option("-o, --output <file>", "write the crx content to <file> instead of stdout").option("--zip-output <file>", "write the zip content to <file>").option("-p, --private-key <file>", "relative path to private key [key.pem], defaults to [directory/../key.pem]").option("-c, --crx-version [number]", "CRX format version, can be either 2 or 3, defaults to 3", parseInt).action(pack);
|
|
14
|
+
program.parse(process.argv);
|
|
15
|
+
/**
|
|
16
|
+
* Generate a new key file
|
|
17
|
+
* @param keyPath path of the key file to create
|
|
18
|
+
* @param opts
|
|
19
|
+
*/
|
|
20
|
+
function generateKeyFile(keyPath, opts) {
|
|
21
|
+
const { privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
22
|
+
modulusLength: 2048,
|
|
23
|
+
publicKeyEncoding: {
|
|
24
|
+
type: "spki",
|
|
25
|
+
format: "der"
|
|
26
|
+
},
|
|
27
|
+
privateKeyEncoding: {
|
|
28
|
+
type: `pkcs${opts.crxVersion === 2 ? "1" : "8"}`,
|
|
29
|
+
format: "pem"
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return fs.promises.writeFile(keyPath, privateKey);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generates a Private Key
|
|
36
|
+
*/
|
|
37
|
+
async function keygen(dir, opts) {
|
|
38
|
+
dir = dir ? path.resolve(cwd, dir) : cwd;
|
|
39
|
+
const keyPath = path.join(dir, "key.pem");
|
|
40
|
+
try {
|
|
41
|
+
fs.accessSync(keyPath);
|
|
42
|
+
if (!opts.force) throw new Error("key.pem already exists in the given location.");
|
|
43
|
+
} catch (_err) {
|
|
44
|
+
await generateKeyFile(keyPath, opts);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function pack(dir, opts) {
|
|
48
|
+
const input = dir ? path.resolve(cwd, dir) : cwd;
|
|
49
|
+
const keyPath = opts.privateKey ? path.resolve(cwd, opts.privateKey) : path.join(input, "..", "key.pem");
|
|
50
|
+
let output;
|
|
51
|
+
if (opts.output) {
|
|
52
|
+
if (path.extname(opts.output) !== ".crx") throw new Error(`-o file is expected to have a \`.crx\` suffix: [${opts.output}] was given.`);
|
|
53
|
+
}
|
|
54
|
+
if (opts.zipOutput) {
|
|
55
|
+
if (path.extname(opts.zipOutput) !== ".zip") throw new Error(`--zip-output file is expected to have a \`.zip\` suffix: [${opts.zipOutput}] was given.`);
|
|
56
|
+
}
|
|
57
|
+
const crx = new ChromeExtension({
|
|
58
|
+
rootDirectory: input,
|
|
59
|
+
version: opts.crxVersion || 3
|
|
60
|
+
});
|
|
61
|
+
await fs.promises.readFile(keyPath).then(null, async (err) => {
|
|
62
|
+
if (err.code === "ENOENT") {
|
|
63
|
+
await generateKeyFile(keyPath, opts);
|
|
64
|
+
process.stderr.write(`Created new private key at: ${keyPath}.\n`);
|
|
65
|
+
return fs.readFileSync(keyPath);
|
|
66
|
+
} else throw err;
|
|
67
|
+
}).then(async (key) => {
|
|
68
|
+
crx.privateKey = key;
|
|
69
|
+
await crx.load();
|
|
70
|
+
const fileBuffer = await crx.loadContents();
|
|
71
|
+
if (opts.zipOutput) {
|
|
72
|
+
const outFile = path.resolve(cwd, opts.zipOutput);
|
|
73
|
+
fs.createWriteStream(outFile).end(fileBuffer);
|
|
74
|
+
} else {
|
|
75
|
+
const crxBuffer = await crx.pack(fileBuffer);
|
|
76
|
+
if (opts.zipOutput) return;
|
|
77
|
+
else if (opts.output) output = opts.output;
|
|
78
|
+
else output = `${path.basename(cwd)}.crx`;
|
|
79
|
+
const outFile = path.resolve(cwd, output);
|
|
80
|
+
if (outFile) fs.createWriteStream(outFile).end(crxBuffer);
|
|
81
|
+
else process.stdout.end(crxBuffer);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
var cli_default = program;
|
|
86
|
+
|
|
87
|
+
//#endregion
|
|
88
|
+
export { cli_default as default };
|
|
89
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":["packageJson.version","output: string"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport path from \"node:path\";\nimport fs from \"node:fs\";\nimport crypto from \"node:crypto\";\n\nimport { program } from \"commander\";\nimport ChromeExtension from \"./index.ts\";\n\nimport packageJson from \"../package.json\" with { type: \"json\" };\n\nconst cwd = process.cwd();\n\nprogram.version(packageJson.version);\n// coming soon\n// .option(\"-x, --xml\", \"output autoupdate xml instead of extension \")\n\ninterface InterfaceCli {\n crxVersion?: number;\n force: boolean;\n privateKey: string;\n output?: string;\n zipOutput?: string;\n //maxBuffer?: number;\n}\n\nprogram\n .command(\"keygen [directory]\")\n .option(\"--force\", \"overwrite the private key if it exists\")\n .option(\n \"-c, --crx-version [number]\",\n \"CRX format version, can be either 2 or 3, defaults to 3\",\n parseInt,\n )\n .description(\"generate a private key in [directory]/key.pem\")\n .action(keygen);\n\nprogram\n .command(\"pack [directory]\")\n .description(\"pack [directory] into a .crx extension\")\n .option(\n \"-o, --output <file>\",\n \"write the crx content to <file> instead of stdout\",\n )\n .option(\"--zip-output <file>\", \"write the zip content to <file>\")\n .option(\n \"-p, --private-key <file>\",\n \"relative path to private key [key.pem], defaults to [directory/../key.pem]\")\n //.option(\n // \"-b, --max-buffer <total>\",\n // \"max amount of memory allowed to generate the crx, in byte\",\n //)\n .option(\n \"-c, --crx-version [number]\",\n \"CRX format version, can be either 2 or 3, defaults to 3\",\n parseInt,\n )\n .action(pack);\n\nprogram.parse(process.argv);\n\n/**\n * Generate a new key file\n * @param keyPath path of the key file to create\n * @param opts\n */\nfunction generateKeyFile(keyPath: string, opts: InterfaceCli): Promise<void> {\n const { privateKey } = crypto.generateKeyPairSync(\"rsa\", {\n modulusLength: 2048,\n publicKeyEncoding: {\n type: \"spki\",\n format: \"der\",\n },\n privateKeyEncoding: {\n // Chromium (tested on 72.0.3626.109) which generates CRX v3 files requires pkcs8 key\n type: `pkcs${opts.crxVersion === 2 ? \"1\" : \"8\"}`,\n format: \"pem\",\n },\n });\n return fs.promises.writeFile(keyPath, privateKey);\n}\n\n/**\n * Generates a Private Key\n */\nasync function keygen(dir: string, opts: InterfaceCli) {\n dir = dir ? path.resolve(cwd, dir) : cwd;\n\n const keyPath = path.join(dir, \"key.pem\");\n\n try {\n fs.accessSync(keyPath);\n if (!opts.force) {\n throw new Error(\"key.pem already exists in the given location.\");\n }\n }\n catch (_err) {\n await generateKeyFile(keyPath, opts);\n }\n}\n\nasync function pack(dir: string, opts: InterfaceCli) {\n const input = dir ? path.resolve(cwd, dir) : cwd;\n const keyPath = opts.privateKey\n ? path.resolve(cwd, opts.privateKey)\n : path.join(input, \"..\", \"key.pem\");\n let output: string;\n\n if (opts.output) {\n if (path.extname(opts.output) !== \".crx\") {\n throw new Error(\n `-o file is expected to have a \\`.crx\\` suffix: [${opts.output}] was given.`,\n );\n }\n }\n\n if (opts.zipOutput) {\n if (path.extname(opts.zipOutput) !== \".zip\") {\n throw new Error(\n `--zip-output file is expected to have a \\`.zip\\` suffix: [${opts.zipOutput}] was given.`,\n );\n }\n }\n\n const crx = new ChromeExtension({\n rootDirectory: input,\n //maxBuffer: opts.maxBuffer,\n version: opts.crxVersion || 3,\n });\n\n await fs.promises.readFile(keyPath)\n .then(null, async (err: unknown) => {\n // If the key file doesn't exist, create one\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n await generateKeyFile(keyPath, opts);\n process.stderr.write(`Created new private key at: ${keyPath}.\\n`);\n return fs.readFileSync(keyPath);\n }\n else {\n throw err;\n }\n })\n .then(async (key) => {\n crx.privateKey = key;\n await crx.load();\n const fileBuffer = await crx.loadContents();\n if (opts.zipOutput) {\n const outFile = path.resolve(cwd, opts.zipOutput);\n\n fs.createWriteStream(outFile).end(fileBuffer);\n }\n else {\n const crxBuffer = await crx.pack(fileBuffer);\n if (opts.zipOutput) {\n return;\n }\n else if (opts.output) {\n output = opts.output;\n }\n else {\n output = `${path.basename(cwd)}.crx`;\n }\n\n const outFile = path.resolve(cwd, output);\n if (outFile) {\n fs.createWriteStream(outFile).end(crxBuffer);\n }\n else {\n process.stdout.end(crxBuffer);\n }\n }\n });\n}\n\nexport default program;\n"],"mappings":";;;;;;;;;AAWA,MAAM,MAAM,QAAQ,KAAK;AAEzB,QAAQ,QAAQA,QAAoB;AAapC,QACG,QAAQ,qBAAqB,CAC7B,OAAO,WAAW,yCAAyC,CAC3D,OACC,8BACA,2DACA,SACD,CACA,YAAY,gDAAgD,CAC5D,OAAO,OAAO;AAEjB,QACG,QAAQ,mBAAmB,CAC3B,YAAY,yCAAyC,CACrD,OACC,uBACA,oDACD,CACA,OAAO,uBAAuB,kCAAkC,CAChE,OACC,4BACA,6EAA6E,CAK9E,OACC,8BACA,2DACA,SACD,CACA,OAAO,KAAK;AAEf,QAAQ,MAAM,QAAQ,KAAK;;;;;;AAO3B,SAAS,gBAAgB,SAAiB,MAAmC;CAC3E,MAAM,EAAE,eAAe,OAAO,oBAAoB,OAAO;EACvD,eAAe;EACf,mBAAmB;GACjB,MAAM;GACN,QAAQ;GACT;EACD,oBAAoB;GAElB,MAAM,OAAO,KAAK,eAAe,IAAI,MAAM;GAC3C,QAAQ;GACT;EACF,CAAC;AACF,QAAO,GAAG,SAAS,UAAU,SAAS,WAAW;;;;;AAMnD,eAAe,OAAO,KAAa,MAAoB;AACrD,OAAM,MAAM,KAAK,QAAQ,KAAK,IAAI,GAAG;CAErC,MAAM,UAAU,KAAK,KAAK,KAAK,UAAU;AAEzC,KAAI;AACF,KAAG,WAAW,QAAQ;AACtB,MAAI,CAAC,KAAK,MACR,OAAM,IAAI,MAAM,gDAAgD;UAG7D,MAAM;AACX,QAAM,gBAAgB,SAAS,KAAK;;;AAIxC,eAAe,KAAK,KAAa,MAAoB;CACnD,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,IAAI,GAAG;CAC7C,MAAM,UAAU,KAAK,aACjB,KAAK,QAAQ,KAAK,KAAK,WAAW,GAClC,KAAK,KAAK,OAAO,MAAM,UAAU;CACrC,IAAIC;AAEJ,KAAI,KAAK,QACP;MAAI,KAAK,QAAQ,KAAK,OAAO,KAAK,OAChC,OAAM,IAAI,MACR,mDAAmD,KAAK,OAAO,cAChE;;AAIL,KAAI,KAAK,WACP;MAAI,KAAK,QAAQ,KAAK,UAAU,KAAK,OACnC,OAAM,IAAI,MACR,6DAA6D,KAAK,UAAU,cAC7E;;CAIL,MAAM,MAAM,IAAI,gBAAgB;EAC9B,eAAe;EAEf,SAAS,KAAK,cAAc;EAC7B,CAAC;AAEF,OAAM,GAAG,SAAS,SAAS,QAAQ,CAChC,KAAK,MAAM,OAAO,QAAiB;AAElC,MAAK,IAA8B,SAAS,UAAU;AACpD,SAAM,gBAAgB,SAAS,KAAK;AACpC,WAAQ,OAAO,MAAM,+BAA+B,QAAQ,KAAK;AACjE,UAAO,GAAG,aAAa,QAAQ;QAG/B,OAAM;GAER,CACD,KAAK,OAAO,QAAQ;AACnB,MAAI,aAAa;AACjB,QAAM,IAAI,MAAM;EAChB,MAAM,aAAa,MAAM,IAAI,cAAc;AAC3C,MAAI,KAAK,WAAW;GAClB,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,UAAU;AAEjD,MAAG,kBAAkB,QAAQ,CAAC,IAAI,WAAW;SAE1C;GACH,MAAM,YAAY,MAAM,IAAI,KAAK,WAAW;AAC5C,OAAI,KAAK,UACP;YAEO,KAAK,OACZ,UAAS,KAAK;OAGd,UAAS,GAAG,KAAK,SAAS,IAAI,CAAC;GAGjC,MAAM,UAAU,KAAK,QAAQ,KAAK,OAAO;AACzC,OAAI,QACF,IAAG,kBAAkB,QAAQ,CAAC,IAAI,UAAU;OAG5C,SAAQ,OAAO,IAAI,UAAU;;GAGjC;;AAGN,kBAAe"}
|
package/dist/crx2.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
//#region src/crx2.ts
|
|
4
|
+
/**
|
|
5
|
+
* Generates and returns a signed package from extension content.
|
|
6
|
+
*
|
|
7
|
+
* BC BREAK `this.package` is not stored anymore (since 1.0.0)
|
|
8
|
+
*/
|
|
9
|
+
function generatePackage(privateKey, publicKey, contents) {
|
|
10
|
+
const signature = generateSignature(privateKey, contents);
|
|
11
|
+
const keyLength = publicKey.length;
|
|
12
|
+
const sigLength = signature.length;
|
|
13
|
+
const zipLength = contents.length;
|
|
14
|
+
const length = 16 + keyLength + sigLength + zipLength;
|
|
15
|
+
const crx = Buffer.alloc(length);
|
|
16
|
+
crx.write("Cr24" + new Array(13).join("\0"), "binary");
|
|
17
|
+
crx[4] = 2;
|
|
18
|
+
crx.writeUInt32LE(keyLength, 8);
|
|
19
|
+
crx.writeUInt32LE(sigLength, 12);
|
|
20
|
+
publicKey.copy(crx, 16);
|
|
21
|
+
signature.copy(crx, 16 + keyLength);
|
|
22
|
+
contents.copy(crx, 16 + keyLength + sigLength);
|
|
23
|
+
return crx;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generates a SHA1 package signature.
|
|
27
|
+
*
|
|
28
|
+
* BC BREAK `this.signature` is not stored anymore (since 1.0.0)
|
|
29
|
+
*/
|
|
30
|
+
function generateSignature(privateKey, contents) {
|
|
31
|
+
return crypto.createSign("sha1").update(contents).sign(privateKey);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { generatePackage as default };
|
|
36
|
+
//# sourceMappingURL=crx2.js.map
|
package/dist/crx2.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crx2.js","names":[],"sources":["../src/crx2.ts"],"sourcesContent":["\"use strict\";\n\nimport crypto from \"node:crypto\";\n\n/**\n * Generates and returns a signed package from extension content.\n *\n * BC BREAK `this.package` is not stored anymore (since 1.0.0)\n */\nexport default function generatePackage(privateKey: crypto.KeyLike, publicKey: Buffer, contents: Buffer): Buffer {\n const signature = generateSignature(privateKey, contents);\n\n const keyLength = publicKey.length;\n const sigLength = signature.length;\n const zipLength = contents.length;\n const length = 16 + keyLength + sigLength + zipLength;\n\n const crx = Buffer.alloc(length);\n\n crx.write(\"Cr24\" + new Array(13).join(\"\\x00\"), \"binary\");\n\n crx[4] = 2;\n crx.writeUInt32LE(keyLength, 8);\n crx.writeUInt32LE(sigLength, 12);\n\n publicKey.copy(crx, 16);\n signature.copy(crx, 16 + keyLength);\n contents.copy(crx, 16 + keyLength + sigLength);\n\n return crx;\n};\n\n/**\n * Generates a SHA1 package signature.\n *\n * BC BREAK `this.signature` is not stored anymore (since 1.0.0)\n */\nfunction generateSignature(privateKey: crypto.KeyLike, contents: Buffer): Buffer {\n return crypto\n .createSign(\"sha1\")\n .update(contents)\n .sign(privateKey);\n}\n"],"mappings":";;;;;;;;AASA,SAAwB,gBAAgB,YAA4B,WAAmB,UAA0B;CAC/G,MAAM,YAAY,kBAAkB,YAAY,SAAS;CAEzD,MAAM,YAAY,UAAU;CAC5B,MAAM,YAAY,UAAU;CAC5B,MAAM,YAAY,SAAS;CAC3B,MAAM,SAAS,KAAK,YAAY,YAAY;CAE5C,MAAM,MAAM,OAAO,MAAM,OAAO;AAEhC,KAAI,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC,KAAK,KAAO,EAAE,SAAS;AAExD,KAAI,KAAK;AACT,KAAI,cAAc,WAAW,EAAE;AAC/B,KAAI,cAAc,WAAW,GAAG;AAEhC,WAAU,KAAK,KAAK,GAAG;AACvB,WAAU,KAAK,KAAK,KAAK,UAAU;AACnC,UAAS,KAAK,KAAK,KAAK,YAAY,UAAU;AAE9C,QAAO;;;;;;;AAQT,SAAS,kBAAkB,YAA4B,UAA0B;AAC/E,QAAO,OACJ,WAAW,OAAO,CAClB,OAAO,SAAS,CAChB,KAAK,WAAW"}
|
package/dist/crx3.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { writeCrxFileHeader, writeSignedData } from "./crx3.pb.js";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import PBf from "pbf";
|
|
4
|
+
|
|
5
|
+
//#region src/crx3.ts
|
|
6
|
+
/**
|
|
7
|
+
* Generates and returns a signed package from extension content.
|
|
8
|
+
*
|
|
9
|
+
* Based on `crx_creator` from Chromium project.
|
|
10
|
+
*
|
|
11
|
+
* @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx_creator.cc}
|
|
12
|
+
*/
|
|
13
|
+
function generatePackage(privateKey, publicKey, contents) {
|
|
14
|
+
let pb;
|
|
15
|
+
pb = new PBf();
|
|
16
|
+
writeSignedData({ crx_id: getCrxId(publicKey) }, pb);
|
|
17
|
+
const signedHeaderData = pb.finish();
|
|
18
|
+
pb = new PBf();
|
|
19
|
+
writeCrxFileHeader({
|
|
20
|
+
sha256_with_rsa: [{
|
|
21
|
+
public_key: publicKey,
|
|
22
|
+
signature: generateSignature(privateKey, signedHeaderData, contents)
|
|
23
|
+
}],
|
|
24
|
+
signed_header_data: signedHeaderData
|
|
25
|
+
}, pb);
|
|
26
|
+
const header = Buffer.from(pb.finish());
|
|
27
|
+
const size = kSignature.length + kVersion.length + SIZE_BYTES + header.length + contents.length;
|
|
28
|
+
const result = Buffer.allocUnsafe(size);
|
|
29
|
+
let index = 0;
|
|
30
|
+
kSignature.copy(result, index);
|
|
31
|
+
kVersion.copy(result, index += kSignature.length);
|
|
32
|
+
result.writeUInt32LE(header.length, index += kVersion.length);
|
|
33
|
+
header.copy(result, index += SIZE_BYTES);
|
|
34
|
+
contents.copy(result, index += header.length);
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* CRX IDs are 16 bytes long
|
|
39
|
+
*/
|
|
40
|
+
const CRX_ID_SIZE = 16;
|
|
41
|
+
/**
|
|
42
|
+
* CRX3 uses 32bit numbers in various places,
|
|
43
|
+
* so let's prepare size constant for that.
|
|
44
|
+
*/
|
|
45
|
+
const SIZE_BYTES = 4;
|
|
46
|
+
/**
|
|
47
|
+
* Used for file format.
|
|
48
|
+
* @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}
|
|
49
|
+
*/
|
|
50
|
+
const kSignature = Buffer.from("Cr24", "utf8");
|
|
51
|
+
/**
|
|
52
|
+
* Used for file format.
|
|
53
|
+
* @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}
|
|
54
|
+
*/
|
|
55
|
+
const kVersion = Buffer.from([
|
|
56
|
+
3,
|
|
57
|
+
0,
|
|
58
|
+
0,
|
|
59
|
+
0
|
|
60
|
+
]);
|
|
61
|
+
/**
|
|
62
|
+
* Used for generating package signatures.
|
|
63
|
+
* @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}
|
|
64
|
+
*/
|
|
65
|
+
const kSignatureContext = Buffer.from("CRX3 SignedData\0", "utf8");
|
|
66
|
+
/**
|
|
67
|
+
* Given public key data, returns CRX ID.
|
|
68
|
+
*/
|
|
69
|
+
function getCrxId(publicKey) {
|
|
70
|
+
const hash = crypto.createHash("sha256");
|
|
71
|
+
hash.update(publicKey);
|
|
72
|
+
return hash.digest().subarray(0, CRX_ID_SIZE);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generates and returns a signature.
|
|
76
|
+
*/
|
|
77
|
+
function generateSignature(privateKey, signedHeaderData, contents) {
|
|
78
|
+
const hash = crypto.createSign("sha256");
|
|
79
|
+
hash.update(kSignatureContext);
|
|
80
|
+
const sizeOctets = Buffer.allocUnsafe(SIZE_BYTES);
|
|
81
|
+
sizeOctets.writeUInt32LE(signedHeaderData.length, 0);
|
|
82
|
+
hash.update(sizeOctets);
|
|
83
|
+
hash.update(signedHeaderData);
|
|
84
|
+
hash.update(contents);
|
|
85
|
+
return Buffer.from(hash.sign(privateKey).toString(), "binary");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
export { generatePackage as default };
|
|
90
|
+
//# sourceMappingURL=crx3.js.map
|
package/dist/crx3.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crx3.js","names":["pb: PBf"],"sources":["../src/crx3.ts"],"sourcesContent":["\"use strict\";\n\nimport crypto from \"node:crypto\";\nimport PBf from \"pbf\";\nimport * as crx from \"./crx3.pb.js\";\n\n/**\n * Generates and returns a signed package from extension content.\n *\n * Based on `crx_creator` from Chromium project.\n *\n * @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx_creator.cc}\n */\nexport default function generatePackage(privateKey: crypto.KeyLike, publicKey: Buffer, contents: Buffer): Buffer {\n let pb: PBf;\n\n pb = new PBf();\n crx.writeSignedData({\n crx_id: getCrxId(publicKey),\n }, pb);\n const signedHeaderData = pb.finish();\n\n pb = new PBf();\n crx.writeCrxFileHeader({\n sha256_with_rsa: [{\n public_key: publicKey,\n signature: generateSignature(privateKey, signedHeaderData, contents),\n }],\n signed_header_data: signedHeaderData,\n }, pb);\n const header = Buffer.from(pb.finish());\n\n const size\n = kSignature.length // Magic constant\n + kVersion.length // Version number\n + SIZE_BYTES // Header size\n + header.length\n + contents.length;\n\n const result = Buffer.allocUnsafe(size);\n\n let index = 0;\n kSignature.copy(result, index);\n kVersion.copy(result, index += kSignature.length);\n result.writeUInt32LE(header.length, index += kVersion.length);\n header.copy(result, index += SIZE_BYTES);\n contents.copy(result, index += header.length);\n\n return result;\n};\n\n/**\n * CRX IDs are 16 bytes long\n */\nconst CRX_ID_SIZE = 16;\n\n/**\n * CRX3 uses 32bit numbers in various places,\n * so let's prepare size constant for that.\n */\nconst SIZE_BYTES = 4;\n\n/**\n * Used for file format.\n * @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}\n */\nconst kSignature = Buffer.from(\"Cr24\", \"utf8\");\n\n/**\n * Used for file format.\n * @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}\n */\nconst kVersion = Buffer.from([3, 0, 0, 0]);\n\n/**\n * Used for generating package signatures.\n * @see {@link https://github.com/chromium/chromium/blob/master/components/crx_file/crx3.proto}\n */\nconst kSignatureContext = Buffer.from(\"CRX3 SignedData\\x00\", \"utf8\");\n\n/**\n * Given public key data, returns CRX ID.\n */\nfunction getCrxId(publicKey: Buffer): Buffer {\n const hash = crypto.createHash(\"sha256\");\n hash.update(publicKey);\n return hash.digest().subarray(0, CRX_ID_SIZE);\n}\n\n/**\n* Generates and returns a signature.\n*/\nfunction generateSignature(privateKey: crypto.KeyLike, signedHeaderData: Uint8Array, contents: Buffer): Buffer {\n const hash = crypto.createSign(\"sha256\");\n\n // Magic constant\n hash.update(kSignatureContext);\n\n // Size of signed_header_data\n const sizeOctets = Buffer.allocUnsafe(SIZE_BYTES);\n sizeOctets.writeUInt32LE(signedHeaderData.length, 0);\n hash.update(sizeOctets);\n\n // Content of signed_header_data\n hash.update(signedHeaderData);\n\n // ZIP content\n hash.update(contents);\n\n return Buffer.from(hash.sign(privateKey).toString(), \"binary\");\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAwB,gBAAgB,YAA4B,WAAmB,UAA0B;CAC/G,IAAIA;AAEJ,MAAK,IAAI,KAAK;AACd,iBAAoB,EAClB,QAAQ,SAAS,UAAU,EAC5B,EAAE,GAAG;CACN,MAAM,mBAAmB,GAAG,QAAQ;AAEpC,MAAK,IAAI,KAAK;AACd,oBAAuB;EACrB,iBAAiB,CAAC;GAChB,YAAY;GACZ,WAAW,kBAAkB,YAAY,kBAAkB,SAAS;GACrE,CAAC;EACF,oBAAoB;EACrB,EAAE,GAAG;CACN,MAAM,SAAS,OAAO,KAAK,GAAG,QAAQ,CAAC;CAEvC,MAAM,OACF,WAAW,SACT,SAAS,SACT,aACA,OAAO,SACP,SAAS;CAEf,MAAM,SAAS,OAAO,YAAY,KAAK;CAEvC,IAAI,QAAQ;AACZ,YAAW,KAAK,QAAQ,MAAM;AAC9B,UAAS,KAAK,QAAQ,SAAS,WAAW,OAAO;AACjD,QAAO,cAAc,OAAO,QAAQ,SAAS,SAAS,OAAO;AAC7D,QAAO,KAAK,QAAQ,SAAS,WAAW;AACxC,UAAS,KAAK,QAAQ,SAAS,OAAO,OAAO;AAE7C,QAAO;;;;;AAMT,MAAM,cAAc;;;;;AAMpB,MAAM,aAAa;;;;;AAMnB,MAAM,aAAa,OAAO,KAAK,QAAQ,OAAO;;;;;AAM9C,MAAM,WAAW,OAAO,KAAK;CAAC;CAAG;CAAG;CAAG;CAAE,CAAC;;;;;AAM1C,MAAM,oBAAoB,OAAO,KAAK,qBAAuB,OAAO;;;;AAKpE,SAAS,SAAS,WAA2B;CAC3C,MAAM,OAAO,OAAO,WAAW,SAAS;AACxC,MAAK,OAAO,UAAU;AACtB,QAAO,KAAK,QAAQ,CAAC,SAAS,GAAG,YAAY;;;;;AAM/C,SAAS,kBAAkB,YAA4B,kBAA8B,UAA0B;CAC7G,MAAM,OAAO,OAAO,WAAW,SAAS;AAGxC,MAAK,OAAO,kBAAkB;CAG9B,MAAM,aAAa,OAAO,YAAY,WAAW;AACjD,YAAW,cAAc,iBAAiB,QAAQ,EAAE;AACpD,MAAK,OAAO,WAAW;AAGvB,MAAK,OAAO,iBAAiB;AAG7B,MAAK,OAAO,SAAS;AAErB,QAAO,OAAO,KAAK,KAAK,KAAK,WAAW,CAAC,UAAU,EAAE,SAAS"}
|
package/dist/crx3.pb.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/crx3.pb.js
|
|
2
|
+
function writeCrxFileHeader(obj, pbf) {
|
|
3
|
+
if (obj.sha256_with_rsa) for (const item of obj.sha256_with_rsa) pbf.writeMessage(2, writeAsymmetricKeyProof, item);
|
|
4
|
+
if (obj.sha256_with_ecdsa) for (const item of obj.sha256_with_ecdsa) pbf.writeMessage(3, writeAsymmetricKeyProof, item);
|
|
5
|
+
if (obj.signed_header_data != null) pbf.writeBytesField(1e4, obj.signed_header_data);
|
|
6
|
+
}
|
|
7
|
+
function writeAsymmetricKeyProof(obj, pbf) {
|
|
8
|
+
if (obj.public_key != null) pbf.writeBytesField(1, obj.public_key);
|
|
9
|
+
if (obj.signature != null) pbf.writeBytesField(2, obj.signature);
|
|
10
|
+
}
|
|
11
|
+
function writeSignedData(obj, pbf) {
|
|
12
|
+
if (obj.crx_id != null) pbf.writeBytesField(1, obj.crx_id);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { writeCrxFileHeader, writeSignedData };
|
|
17
|
+
//# sourceMappingURL=crx3.pb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crx3.pb.js","names":[],"sources":["../src/crx3.pb.js"],"sourcesContent":["// code generated by pbf v4.0.1 from https://github.com/chromium/chromium/blob/e4a3bada6aab7aed90460ec7d27f8c7167c5666e/components/crx_file/crx3.proto\n\nexport function readCrxFileHeader(pbf, end) {\n return pbf.readFields(readCrxFileHeaderField, {sha256_with_rsa: [], sha256_with_ecdsa: [], signed_header_data: undefined}, end);\n}\nfunction readCrxFileHeaderField(tag, obj, pbf) {\n if (tag === 2) obj.sha256_with_rsa.push(readAsymmetricKeyProof(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 3) obj.sha256_with_ecdsa.push(readAsymmetricKeyProof(pbf, pbf.readVarint() + pbf.pos));\n else if (tag === 10000) obj.signed_header_data = pbf.readBytes();\n}\nexport function writeCrxFileHeader(obj, pbf) {\n if (obj.sha256_with_rsa) for (const item of obj.sha256_with_rsa) pbf.writeMessage(2, writeAsymmetricKeyProof, item);\n if (obj.sha256_with_ecdsa) for (const item of obj.sha256_with_ecdsa) pbf.writeMessage(3, writeAsymmetricKeyProof, item);\n if (obj.signed_header_data != null) pbf.writeBytesField(10000, obj.signed_header_data);\n}\n\nexport function readAsymmetricKeyProof(pbf, end) {\n return pbf.readFields(readAsymmetricKeyProofField, {public_key: undefined, signature: undefined}, end);\n}\nfunction readAsymmetricKeyProofField(tag, obj, pbf) {\n if (tag === 1) obj.public_key = pbf.readBytes();\n else if (tag === 2) obj.signature = pbf.readBytes();\n}\nexport function writeAsymmetricKeyProof(obj, pbf) {\n if (obj.public_key != null) pbf.writeBytesField(1, obj.public_key);\n if (obj.signature != null) pbf.writeBytesField(2, obj.signature);\n}\n\nexport function readSignedData(pbf, end) {\n return pbf.readFields(readSignedDataField, {crx_id: undefined}, end);\n}\nfunction readSignedDataField(tag, obj, pbf) {\n if (tag === 1) obj.crx_id = pbf.readBytes();\n}\nexport function writeSignedData(obj, pbf) {\n if (obj.crx_id != null) pbf.writeBytesField(1, obj.crx_id);\n}\n"],"mappings":";AAUA,SAAgB,mBAAmB,KAAK,KAAK;AACzC,KAAI,IAAI,gBAAiB,MAAK,MAAM,QAAQ,IAAI,gBAAiB,KAAI,aAAa,GAAG,yBAAyB,KAAK;AACnH,KAAI,IAAI,kBAAmB,MAAK,MAAM,QAAQ,IAAI,kBAAmB,KAAI,aAAa,GAAG,yBAAyB,KAAK;AACvH,KAAI,IAAI,sBAAsB,KAAM,KAAI,gBAAgB,KAAO,IAAI,mBAAmB;;AAU1F,SAAgB,wBAAwB,KAAK,KAAK;AAC9C,KAAI,IAAI,cAAc,KAAM,KAAI,gBAAgB,GAAG,IAAI,WAAW;AAClE,KAAI,IAAI,aAAa,KAAM,KAAI,gBAAgB,GAAG,IAAI,UAAU;;AASpE,SAAgB,gBAAgB,KAAK,KAAK;AACtC,KAAI,IAAI,UAAU,KAAM,KAAI,gBAAgB,GAAG,IAAI,OAAO"}
|