Skip to content

Commit bd491df

Browse files
armfazhthibmeu
authored andcommitted
Generating crx extension and assets.
1 parent ede1624 commit bd491df

File tree

7 files changed

+125
-28
lines changed

7 files changed

+125
-28
lines changed

examples/browser-extension/README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,39 +20,50 @@ Chrome browser extension adding HTTP Message Signature on all outgoing requests
2020

2121
## Usage
2222

23-
If you don't have one, generate a signing key for your extension
23+
Compile the code of the extension:
2424

2525
```shell
26-
npm run generate-signing-key
26+
npm run build:chrome
2727
```
2828

29-
Then build, bundle, and sign the Chrome extension
29+
Bundle and sign the Chrome extension:
3030

3131
```shell
3232
npm run bundle:chrome
3333
```
3434

35-
This extension requires an [entreprise policy](https://support.google.com/chrome/a/answer/187202?hl=en) to be configured on your Chrome. It requires that you configure your Chrome instance with a policy to force install the extension.
35+
This command creates the folder `dist` with both the unpacked and packed (.crx) Chrome extension.
3636

37-
In a distinct terminal, run `npm run start:config`. This ensures Chrome can install your extension.
37+
## Installing Extension from Sources
3838

39-
On Linux
39+
This extension requires the [webRequestBlocking](https://developer.chrome.com/docs/extensions/reference/api/webRequest) permission, which in turn requires of an [Enterprise policy](https://support.google.com/chrome/a/answer/187202?hl=en) to be configured on Chrome.
40+
41+
Follow these steps to configure Chrome with such a policy and force it to install the extension locally.
42+
First, run in another terminal:
43+
44+
```shell
45+
npm run start:config
46+
```
47+
48+
This starts a server at http://localhost:8000 for installing extension locally.
49+
50+
Then, copy the policy file in the correspondent system path:
51+
52+
On Linux:
4053

4154
```shell
4255
mkdir -p /etc/opt/chrome/policies/managed
43-
cp config/chromium/policy.json /etc/opt/chrome/policies/managed/policy.json
56+
cp policy/policy.json /etc/opt/chrome/policies/managed/policy.json
4457
```
4558

46-
On macOS
59+
On MacOS:
4760

4861
```shell
4962
mkdir -p /Library/Managed\ Preferences/
50-
cp config/chromium/com.google.Chrome.managed.plist /Library/Managed\ Preferences/
63+
cp policy/com.google.Chrome.managed.plist /Library/Managed\ Preferences/
5164
```
5265

53-
You can confirm the policy is installed by navigating to `chrome://policy/`.
54-
55-
> You might have to change the forced_install ID. To find the ID of your extension, drag and drop it in Chrome, look at the ID, then replace instances of `fkgomfknhcfpepcgkimebggnfgkbghii`
66+
You can confirm the policy is installed by navigating to [chrome://policy/](chrome://policy/) and make sure to reload the policies.
5667

5768
## Security Considerations
5869

examples/browser-extension/config/chromium/update.xml

Lines changed: 0 additions & 6 deletions
This file was deleted.

examples/browser-extension/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "http-message-signatures-extension",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"description": "Enterprise browser extension adding an HTTP Message signature to all requests",
55
"scripts": {
6-
"build:chrome": "tsup src/background.ts --format esm --platform browser --target chrome100 --publicDir platform/mv3/chromium --clean --out-dir dist/mv3/chromium --external node:crypto",
7-
"bundle:chrome": "npm run build:chrome && mkdir -p dist/web-ext-artifacts && crx pack --private-key signing-key.pem --output dist/web-ext-artifacts/http-message-signatures-extension.crx dist/mv3/chromium",
8-
"generate-signing-key": "openssl genrsa -out signing-key.pem 2048",
9-
"start:config": "cp ./config/chromium/update.xml dist/web-ext-artifacts && http-server ./dist/web-ext-artifacts -p 8000",
10-
"test": "echo \"Error: no test specified\" && exit 1"
6+
"build:chrome": "tsup src/background.ts --format esm --platform browser --target chrome100 --clean --out-dir dist/mv3/chromium --external node:crypto",
7+
"bundle:chrome": "npm run build:chrome && node ./scripts/build_web_artifacts.mjs",
8+
"start:config": "http-server ./dist/web-ext-artifacts -p 8000",
9+
"test": "echo \"Error: no test specified\" && exit 1",
10+
"clean": "rimraf dist"
1111
},
1212
"repository": {
1313
"type": "git",
@@ -32,7 +32,7 @@
3232
"@types/libsodium-wrappers": "0.7.14",
3333
"crx": "5.0.1",
3434
"http-server": "14.1.1",
35-
"libsodium-wrappers": "0.7.15",
35+
"rimraf": "6.0.1",
3636
"tsup": "8.5.0"
3737
}
3838
}

examples/browser-extension/platform/mv3/chromium/manifest.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"manifest_version": 3,
33
"name": "HTTP Message Signatures User Agent",
4-
"version": "0.1.9",
54
"permissions": ["webRequest", "webRequestBlocking"],
65
"host_permissions": ["<all_urls>"],
76
"background": {

examples/browser-extension/config/chromium/com.google.Chrome.managed.plist renamed to examples/browser-extension/policy/com.google.Chrome.managed.plist.templ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<key>installation_mode</key>
1010
<string>blocked</string>
1111
</dict>
12-
<key>fkgomfknhcfpepcgkimebggnfgkbghii</key>
12+
<key>********************************</key>
1313
<dict>
1414
<key>installation_mode</key>
1515
<string>force_installed</string>

examples/browser-extension/config/chromium/policy.json renamed to examples/browser-extension/policy/policy.json.templ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"ExtensionSettings": {
3-
"fkgomfknhcfpepcgkimebggnfgkbghii": {
3+
"********************************": {
44
"installation_mode": "force_installed",
55
"update_url": "http://localhost:8000/update.xml"
66
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2025 Cloudflare, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import ChromeExtension from "crx";
16+
import * as fs from "node:fs";
17+
import path from "node:path";
18+
const { KeyObject } = await import("node:crypto");
19+
const { subtle } = globalThis.crypto;
20+
import pkg from '../package.json' with { type: "json" };
21+
22+
function makePolicy(extensionID) {
23+
const MarkerString = "********************************";
24+
const policyPath = path.join(path.dirname("."), "policy");
25+
if (!fs.existsSync(policyPath)) {
26+
fs.mkdirSync(policyPath, { recursive: true });
27+
}
28+
29+
for (let fileName of ["com.google.Chrome.managed.plist", "policy.json"]) {
30+
const template = fs.readFileSync(
31+
path.join(policyPath, fileName + ".templ"),
32+
"utf8"
33+
);
34+
const fileContent = template.split(MarkerString).join(extensionID);
35+
fs.writeFileSync(path.join(policyPath, fileName), fileContent);
36+
}
37+
}
38+
39+
function setManifestVersion(version) {
40+
const manifestInputPath = path.join(path.dirname("."), "platform", "mv3", "chromium", 'manifest.json');
41+
const manifestOutputPath = path.join(path.dirname("."), "dist", "mv3", "chromium", 'manifest.json');
42+
const manifestStr = fs.readFileSync(manifestInputPath, "utf8");
43+
const manifest = JSON.parse(manifestStr);
44+
manifest.version = version;
45+
fs.writeFileSync(manifestOutputPath, JSON.stringify(manifest, null, 2));
46+
}
47+
48+
49+
(async function main() {
50+
51+
const distPath = path.join(path.dirname("."), "dist", "web-ext-artifacts");
52+
if (!fs.existsSync(distPath)) {
53+
fs.mkdirSync(distPath, { recursive: true });
54+
}
55+
56+
const { privateKey, publicKey } = await subtle.generateKey(
57+
{
58+
name: "RSASSA-PKCS1-v1_5",
59+
modulusLength: 2048,
60+
publicExponent: Uint8Array.from([1, 0, 1]),
61+
hash: "SHA-256",
62+
},
63+
true,
64+
["sign", "verify"]
65+
);
66+
67+
const skPEM = KeyObject.from(privateKey).export({
68+
type: "pkcs8",
69+
format: "pem",
70+
});
71+
const pkBytes = KeyObject.from(publicKey).export({
72+
type: "pkcs1",
73+
format: "der",
74+
});
75+
76+
const crx = new ChromeExtension({
77+
codebase: "http://localhost:8000/" + pkg.name + '.crx',
78+
privateKey: skPEM,
79+
publicKey: pkBytes,
80+
});
81+
82+
setManifestVersion(pkg.version);
83+
await crx.load(path.join(path.dirname("."), "dist", "mv3", "chromium"))
84+
const extensionBytes = await crx.pack();
85+
const extensionID = crx.generateAppId();
86+
87+
fs.writeFileSync("private_key.pem", skPEM);
88+
fs.writeFileSync(path.join(distPath, pkg.name + '.crx'), extensionBytes);
89+
fs.writeFileSync(path.join(distPath, "update.xml"), crx.generateUpdateXML());
90+
makePolicy(extensionID);
91+
92+
console.log(`Build Extension with ID: ${extensionID}`)
93+
})();

0 commit comments

Comments
 (0)