Skip to content

Commit 1b5fb2f

Browse files
committed
Chromium build (init)
1 parent 8abfd4c commit 1b5fb2f

File tree

11 files changed

+387
-0
lines changed

11 files changed

+387
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules/
22
**/*.mmdb
33
**/*.d.?ts
4+
assets/chromium/
45
lib/**/index.mjs

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ assets/public/maxmind/GeoLite2-Country.mmdb :
2020
curl -vL "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=${MAXMIND_LICENSE_KEY}&suffix=tar.gz" | tar --strip-components 1 -xzv -C `dirname $@`
2121
ls -lAh `dirname $@`
2222

23+
assets/chromium/manifest.json : package.json
24+
mkdir -p `dirname $@`
25+
bun bin/manifest.ts chromium > $@
26+
27+
assets/chromium/service_worker.js : src/service_worker.ts
28+
mkdir -p `dirname $@`
29+
bun build --entrypoints src/service_worker.ts --outdir assets/chromium
30+
31+
.PHONY : clean
32+
clean :
33+
-rm assets/chromium/{manifest.json,service_worker.js}
34+
2335
.PHONY : distclean
2436
distclean :
2537
-rm assets/public/maxmind/GeoLite2-Country.mmdb

assets/_locales/en/messages.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"ext_description": {
3+
"message": "Capture The Flag — browser extension."
4+
},
5+
"ext_name": {
6+
"message": "Capture The Flag"
7+
},
8+
"ext_short_name": {
9+
"message": "CTF"
10+
}
11+
}

assets/chromium/_locales

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../_locales

assets/chromium/icons

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../icons

bin/manifest.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { argv } from 'node:process';
2+
3+
import _ from 'lodash-es';
4+
5+
import pkg from '../package.json' with { type: 'json' };
6+
import Manifest from '../src/manifest.ts';
7+
8+
if (import.meta.main) {
9+
main();
10+
}
11+
12+
function main(args = argv.slice(2)) {
13+
const manifest = new Manifest(process.env, pkg);
14+
const target = _.first(args) as 'firefox' | 'chromium';
15+
16+
console.log(manifest.render(target));
17+
}

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
"license": "MPL-2.0",
77
"type": "module",
88
"scripts": {
9+
"t": "bun test --watch",
910
"postinstall": "make lib/maxmind"
1011
},
1112
"dependencies": {
1213
"buffer": "^6.0.3",
14+
"lodash-es": "^4.17.21",
1315
"mmdb-lib": "^2.1.1"
1416
},
1517
"devDependencies": {
1618
"@types/bun": "latest",
19+
"@types/lodash-es": "^4.17.12",
20+
"chrome-types": "latest",
1721
"eslint-config-nilfalse": "github:nilfalse/eslint",
1822
"prettier": "^3.3",
1923
"webpack-cli": "^5.1.4"

src/manifest.spec.ts

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { describe, expect, it } from 'bun:test';
2+
3+
import Manifest from './manifest';
4+
5+
describe('manifest', () => {
6+
describe('Firefox', () => {
7+
it('should create manifest v2', async () => {
8+
const manifest = new Manifest(
9+
{},
10+
{ author: 'nilfalse.com', version: '1.0.0' },
11+
);
12+
13+
expect(manifest.render('firefox').split('\n')).toStrictEqual([
14+
'{',
15+
' "manifest_version": 2,',
16+
' "name": "__MSG_ext_name__",',
17+
' "short_name": "__MSG_ext_short_name__",',
18+
' "version": "1.0.0",',
19+
' "default_locale": "en",',
20+
' "description": "__MSG_ext_description__",',
21+
' "icons": {',
22+
' "32": "icons/icon_32px.png",',
23+
' "48": "icons/icon_48px.png",',
24+
' "128": "icons/icon_128px.png",',
25+
' "256": "icons/icon_256px.png",',
26+
' "512": "icons/icon_512px.png"',
27+
' },',
28+
' "page_action": {',
29+
' "default_icon": {',
30+
' "32": "icons/icon_32px.png",',
31+
' "48": "icons/icon_48px.png",',
32+
' "128": "icons/icon_128px.png",',
33+
' "256": "icons/icon_256px.png",',
34+
' "512": "icons/icon_512px.png"',
35+
' },',
36+
' "default_popup": "popup.html",',
37+
' "show_matches": [',
38+
' "<all_urls>"',
39+
' ]',
40+
' },',
41+
' "background": {},',
42+
' "permissions": [',
43+
' "dns",',
44+
' "webRequest",',
45+
' "storage"',
46+
' ],',
47+
' "host_permissions": [',
48+
' "<all_urls>"',
49+
' ],',
50+
' "author": "nilfalse.com",',
51+
' "browser_specific_settings": {',
52+
' "gecko": {',
53+
' "id": "@ctf"',
54+
' }',
55+
' }',
56+
'}',
57+
]);
58+
});
59+
60+
it('should use commit hash for version when it is available', async () => {
61+
const manifest = new Manifest(
62+
{ GITHUB_SHA: '666e666' },
63+
{ author: 'nilfalse.com', version: '1.0.0' },
64+
);
65+
66+
expect(manifest.render('firefox').split('\n')).toStrictEqual([
67+
'{',
68+
' "manifest_version": 2,',
69+
' "name": "__MSG_ext_name__",',
70+
' "short_name": "__MSG_ext_short_name__",',
71+
' "version": "1.0.0",',
72+
' "version_name": "1.0.0 (666e666)",',
73+
' "default_locale": "en",',
74+
' "description": "__MSG_ext_description__",',
75+
' "icons": {',
76+
' "32": "icons/icon_32px.png",',
77+
' "48": "icons/icon_48px.png",',
78+
' "128": "icons/icon_128px.png",',
79+
' "256": "icons/icon_256px.png",',
80+
' "512": "icons/icon_512px.png"',
81+
' },',
82+
' "page_action": {',
83+
' "default_icon": {',
84+
' "32": "icons/icon_32px.png",',
85+
' "48": "icons/icon_48px.png",',
86+
' "128": "icons/icon_128px.png",',
87+
' "256": "icons/icon_256px.png",',
88+
' "512": "icons/icon_512px.png"',
89+
' },',
90+
' "default_popup": "popup.html",',
91+
' "show_matches": [',
92+
' "<all_urls>"',
93+
' ]',
94+
' },',
95+
' "background": {},',
96+
' "permissions": [',
97+
' "dns",',
98+
' "webRequest",',
99+
' "storage"',
100+
' ],',
101+
' "host_permissions": [',
102+
' "<all_urls>"',
103+
' ],',
104+
' "author": "nilfalse.com",',
105+
' "browser_specific_settings": {',
106+
' "gecko": {',
107+
' "id": "@ctf"',
108+
' }',
109+
' }',
110+
'}',
111+
]);
112+
});
113+
});
114+
115+
describe('Chromium', () => {
116+
it('should create manifest v3', async () => {
117+
const manifest = new Manifest(
118+
{ GITHUB_SHA: '666e666' },
119+
{ author: 'nilfalse.com', version: '1.0.0' },
120+
);
121+
122+
expect(manifest.render('chromium').split('\n')).toStrictEqual([
123+
'{',
124+
' "manifest_version": 3,',
125+
' "name": "__MSG_ext_name__",',
126+
' "short_name": "__MSG_ext_short_name__",',
127+
' "version": "1.0.0",',
128+
' "version_name": "1.0.0 (666e666)",',
129+
' "default_locale": "en",',
130+
' "description": "__MSG_ext_description__",',
131+
' "icons": {',
132+
' "32": "icons/icon_32px.png",',
133+
' "48": "icons/icon_48px.png",',
134+
' "128": "icons/icon_128px.png",',
135+
' "256": "icons/icon_256px.png",',
136+
' "512": "icons/icon_512px.png"',
137+
' },',
138+
' "action": {',
139+
' "default_icon": {',
140+
' "32": "icons/icon_32px.png",',
141+
' "48": "icons/icon_48px.png",',
142+
' "128": "icons/icon_128px.png",',
143+
' "256": "icons/icon_256px.png",',
144+
' "512": "icons/icon_512px.png"',
145+
' },',
146+
' "default_popup": "popup.html",',
147+
' "show_matches": [',
148+
' "<all_urls>"',
149+
' ]',
150+
' },',
151+
' "background": {',
152+
' "service_worker": "service_worker.js"',
153+
' },',
154+
' "permissions": [',
155+
' "webRequest",',
156+
' "storage",',
157+
' "offscreen"',
158+
' ],',
159+
' "host_permissions": [',
160+
' "<all_urls>"',
161+
' ],',
162+
' "author": "nilfalse.com"',
163+
'}',
164+
]);
165+
});
166+
});
167+
});

src/manifest.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import _ from 'lodash-es';
2+
3+
export default class Manifest {
4+
constructor(
5+
private readonly env: Record<string, string | undefined>,
6+
private readonly pkg: Record<string, unknown>,
7+
) {}
8+
9+
get version() {
10+
const { GITHUB_SHA } = this.env;
11+
const { version } = this.pkg as { version: string };
12+
13+
return GITHUB_SHA && version + ` (${GITHUB_SHA.substring(0, 8)})`;
14+
}
15+
16+
get icons() {
17+
return {
18+
32: 'icons/icon_32px.png',
19+
48: 'icons/icon_48px.png',
20+
128: 'icons/icon_128px.png',
21+
256: 'icons/icon_256px.png',
22+
512: 'icons/icon_512px.png',
23+
};
24+
}
25+
26+
get action() {
27+
return {
28+
default_icon: this.icons,
29+
default_popup: 'popup.html',
30+
show_matches: ['<all_urls>'],
31+
};
32+
}
33+
34+
render(target: 'firefox' | 'chromium') {
35+
const { author, version } = this.pkg;
36+
37+
const manifest = {
38+
manifest_version: undefined,
39+
40+
name: '__MSG_ext_name__',
41+
short_name: '__MSG_ext_short_name__',
42+
version,
43+
version_name: this.version,
44+
45+
default_locale: 'en',
46+
description: '__MSG_ext_description__',
47+
48+
icons: this.icons,
49+
50+
action: undefined,
51+
page_action: undefined,
52+
53+
options_ui: undefined,
54+
// {
55+
// page: 'options.html',
56+
// open_in_tab: false,
57+
// },
58+
59+
background: {
60+
background: undefined,
61+
service_worker: undefined,
62+
},
63+
64+
permissions: ['webRequest', 'storage'],
65+
host_permissions: ['<all_urls>'],
66+
author,
67+
68+
browser_specific_settings: undefined,
69+
};
70+
71+
switch (target) {
72+
case 'firefox':
73+
_.set(manifest, 'manifest_version', 2);
74+
_.set(manifest, 'page_action', this.action);
75+
_.set(manifest, 'browser_specific_settings', {
76+
gecko: {
77+
id: '@ctf',
78+
},
79+
});
80+
81+
manifest.permissions.unshift('dns');
82+
83+
break;
84+
case 'chromium':
85+
_.set(manifest, 'manifest_version', 3);
86+
_.set(manifest, 'background.service_worker', 'service_worker.js');
87+
_.set(manifest, 'action', this.action);
88+
89+
manifest.permissions.push('offscreen');
90+
91+
break;
92+
default:
93+
throw new Error(
94+
`Unknown manifest: ${target}, expected 'firefox' or 'chromium'`,
95+
);
96+
}
97+
98+
return JSON.stringify(manifest, null, 2);
99+
}
100+
}

src/service_worker.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/// <reference lib="WebWorker" />
2+
/// <reference types="chrome-types" />
3+
4+
import * as maxmind from '../lib/maxmind/index.mjs';
5+
6+
chrome.runtime.onInstalled.addListener((details) => {
7+
console.log('Chrome Installing', details.reason);
8+
9+
if (details.reason !== 'install' && details.reason !== 'update') {
10+
return;
11+
}
12+
});
13+
14+
const MAXMIND_URL = 'https://ctf.pages.dev/maxmind/GeoLite2-Country.mmdb';
15+
16+
self.addEventListener('activate', (event) => {
17+
console.log('Activating');
18+
19+
event.waitUntil(
20+
caches
21+
.match(MAXMIND_URL)
22+
.then(
23+
(r) =>
24+
r ||
25+
caches
26+
.open('v1')
27+
.then((cache) => cache.addAll([MAXMIND_URL]))
28+
.then(() => caches.match(MAXMIND_URL)),
29+
)
30+
.then((r) => {
31+
console.log(r);
32+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
33+
return r!;
34+
})
35+
.then(maxmind.init)
36+
.then((reader) => {
37+
console.log('Reader', reader);
38+
39+
console.log(reader.get('185.51.76.136'));
40+
}),
41+
);
42+
});
43+
44+
declare const self: ServiceWorkerGlobalScope;

0 commit comments

Comments
 (0)