browser-extension-manager 1.6.1 → 1.7.0
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/CLAUDE.md +268 -0
- package/README.md +4 -0
- package/dist/commands/install.js +2 -2
- package/dist/commands/setup.js +2 -1
- package/dist/defaults/.github/workflows/publish.yml +4 -1
- package/dist/lib/safe-install.js +13 -0
- package/docs/audit.md +67 -0
- package/docs/auth.md +147 -0
- package/docs/build-system.md +131 -0
- package/docs/cdp-debugging.md +45 -0
- package/docs/cli.md +70 -0
- package/docs/common-mistakes.md +10 -0
- package/docs/components.md +87 -0
- package/docs/css.md +91 -0
- package/docs/defaults.md +54 -0
- package/docs/environment-detection.md +79 -0
- package/docs/extension.md +94 -0
- package/docs/hooks.md +101 -0
- package/docs/icons.md +47 -0
- package/docs/logging.md +33 -0
- package/docs/managers.md +100 -0
- package/docs/offscreen.md +57 -0
- package/docs/publishing.md +175 -0
- package/docs/templating.md +66 -0
- package/docs/test-boot-layer.md +141 -0
- package/docs/test-framework.md +404 -0
- package/docs/themes.md +77 -0
- package/docs/translations.md +72 -0
- package/docs/xss-prevention.md +95 -0
- package/package.json +5 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Translations
|
|
2
|
+
|
|
3
|
+
`npm run build` automatically translates `src/_locales/en/messages.json` to 16 languages via the Claude CLI. Only missing translations are generated — existing translations are preserved.
|
|
4
|
+
|
|
5
|
+
## Languages produced
|
|
6
|
+
|
|
7
|
+
`zh`, `es`, `hi`, `ar`, `pt`, `ru`, `ja`, `de`, `fr`, `ko`, `ur`, `id`, `bn`, `tl`, `vi`, `it`
|
|
8
|
+
|
|
9
|
+
(Output written to `src/_locales/<lang>/messages.json`, then copied to `dist/_locales/` and `packaged/<browser>/raw/_locales/`.)
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
[src/gulp/tasks/translate.js](../src/gulp/tasks/translate.js):
|
|
14
|
+
|
|
15
|
+
1. Reads `src/_locales/en/messages.json` (source of truth — author all your strings here).
|
|
16
|
+
2. For each target language, reads existing `src/_locales/<lang>/messages.json` if present.
|
|
17
|
+
3. Computes the set of keys present in `en` but missing (or empty) in the target language.
|
|
18
|
+
4. Sends the missing keys to Claude CLI (`@anthropic-ai/claude-agent-sdk`) for translation.
|
|
19
|
+
5. Merges results back into the target locale file.
|
|
20
|
+
|
|
21
|
+
Existing translations are NEVER overwritten — once a key is translated, it stays. Edit the target language file directly if you want to change a translation.
|
|
22
|
+
|
|
23
|
+
## What gets translated
|
|
24
|
+
|
|
25
|
+
Keys with a `message` field:
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{
|
|
29
|
+
appName: {
|
|
30
|
+
message: 'Tabblar — Workspace & Tab Manager',
|
|
31
|
+
description: 'The name of the extension.'
|
|
32
|
+
},
|
|
33
|
+
appDescription: {
|
|
34
|
+
message: 'Powerful tab and workspace manager...',
|
|
35
|
+
description: 'The description of the extension.'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `description` field provides context to the translator (helps Claude pick the right translation when a word is ambiguous).
|
|
41
|
+
|
|
42
|
+
## Manifest `__MSG_*__` placeholders
|
|
43
|
+
|
|
44
|
+
`src/manifest.json` references locale keys via `__MSG_<key>__`:
|
|
45
|
+
|
|
46
|
+
```jsonc
|
|
47
|
+
{
|
|
48
|
+
name: '__MSG_appName__',
|
|
49
|
+
description: '__MSG_appDescription__',
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Chrome resolves these at install time using the user's browser locale, falling back to `default_locale` (set in the manifest).
|
|
54
|
+
|
|
55
|
+
The [test-framework.md](test-framework.md) ships a `locales.test.js` pattern that verifies every `__MSG_*__` placeholder in your manifest has a definition in `messages.json` — catches drift between manifest and i18n catalog before users see broken store listings.
|
|
56
|
+
|
|
57
|
+
## Disabling translations
|
|
58
|
+
|
|
59
|
+
Don't want auto-translate? Either:
|
|
60
|
+
|
|
61
|
+
- Don't have `_locales/en/messages.json` (then there's nothing to translate from), or
|
|
62
|
+
- Set the `description` of every key to literal "no-translate" (or comparable convention — adjust `translate.js` to match), or
|
|
63
|
+
- Remove the `translate` task from your gulp pipeline (`gulp/main.js` invocation).
|
|
64
|
+
|
|
65
|
+
## Cost / API key
|
|
66
|
+
|
|
67
|
+
Translations use Claude via `@anthropic-ai/claude-agent-sdk`. You'll need your Claude CLI authenticated (`claude auth`) or the SDK will fail. For most extensions, the cost is one-time per language per key change — typically pennies.
|
|
68
|
+
|
|
69
|
+
## See also
|
|
70
|
+
|
|
71
|
+
- [build-system.md](build-system.md) — gulp pipeline including translate task
|
|
72
|
+
- [publishing.md](publishing.md) — auto-publishing to Chrome / Firefox / Edge stores
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# XSS Prevention (ZERO TRUST — MANDATORY)
|
|
2
|
+
|
|
3
|
+
Zero tolerance for unescaped attacker-controllable strings in HTML. Extensions are especially exposed — content scripts read from arbitrary pages, popups render tab titles / URLs / favicon URLs, and all of those are attacker-controllable. Any string from outside the codebase (tab data, page DOM content, Firestore, API responses, URL params, user input) MUST be escaped before being inserted via `innerHTML`, `insertAdjacentHTML`, `outerHTML`, or attribute interpolation.
|
|
4
|
+
|
|
5
|
+
## The Rule
|
|
6
|
+
|
|
7
|
+
**Canonical form (same as UJM/EM):** `webManager.utilities().escapeHTML(value)` inline at every usage site.
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// ✅ CORRECT — canonical inline form
|
|
11
|
+
import webManager from 'web-manager';
|
|
12
|
+
|
|
13
|
+
$el.innerHTML = `<div>${webManager.utilities().escapeHTML(tab.title)}</div>`;
|
|
14
|
+
$el.innerHTML = `<img src="${webManager.utilities().escapeHTML(tab.favIconUrl)}" alt="">`;
|
|
15
|
+
|
|
16
|
+
// ❌ DANGEROUS — attacker-controlled favicon URL can break out of src=""
|
|
17
|
+
$el.innerHTML = `<img src="${tab.favIconUrl}">`;
|
|
18
|
+
// Malicious site serves: favIconUrl = 'x" onerror="alert(1)'
|
|
19
|
+
// Resulting HTML: <img src="x" onerror="alert(1)"> → executes in extension page context
|
|
20
|
+
|
|
21
|
+
// ❌ DANGEROUS — tab title can contain HTML
|
|
22
|
+
$el.innerHTML = `<h1>${tab.title}</h1>`;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Common extension XSS vectors:**
|
|
26
|
+
|
|
27
|
+
- `tab.title`, `tab.url`, `tab.favIconUrl` from `chrome.tabs.query/get` — any visited page can set these.
|
|
28
|
+
- DOM content read by content scripts and sent back to privileged pages (popup/options/sidepanel).
|
|
29
|
+
- Bookmark titles, history entries, recently-closed tabs — all originate from arbitrary pages.
|
|
30
|
+
- Stored user data (snippets, saved workspaces) if a malicious page can influence what gets stored.
|
|
31
|
+
|
|
32
|
+
## NEVER Write Your Own Escape Function
|
|
33
|
+
|
|
34
|
+
Do NOT:
|
|
35
|
+
|
|
36
|
+
- Alias: `const escape = webManager.utilities().escapeHTML;`
|
|
37
|
+
- Wrap: `const escape = (s) => webManager.utilities().escapeHTML(s);`
|
|
38
|
+
- Destructure: `const { escapeHTML } = webManager.utilities();`
|
|
39
|
+
- `.bind()` it
|
|
40
|
+
- Define a local `escapeHtml`/`escapeHTML` helper in a `utils.js` and import it across files — this is the most common violation in BXM extensions. Delete the helper, add `import webManager from 'web-manager'`, inline the canonical form at every call site.
|
|
41
|
+
|
|
42
|
+
## URLs Must Also Be Sanitized
|
|
43
|
+
|
|
44
|
+
`escapeHTML` alone lets `javascript:alert(1)` through — dynamic URLs in executable sinks MUST also be wrapped in `webManager.utilities().sanitizeURL(url)`, which returns `''` for any non-`http:`/`https:` protocol (blocks `javascript:`, `data:`, `vbscript:`). Extensions often build UI from `tab.url` — a malicious page can set a `javascript:` URL that executes when clicked in the popup.
|
|
45
|
+
|
|
46
|
+
**Must sanitize (can execute JS):**
|
|
47
|
+
|
|
48
|
+
- `<a href>`, `<area href>`, `<base href>`
|
|
49
|
+
- `<iframe src>`
|
|
50
|
+
- `<form action>`, `formaction`
|
|
51
|
+
- `<object data>`
|
|
52
|
+
- `<script src>` (don't do dynamically)
|
|
53
|
+
- SVG `href` / `xlink:href`
|
|
54
|
+
- Property assignments: `.href =`, `.src =` (on above elements), `window.location =`, `location.href =`, `location.replace()`, `location.assign()`, `window.open()`, `extension.tabs.create({ url })` if `url` is dynamic
|
|
55
|
+
|
|
56
|
+
**Do NOT need to sanitize:**
|
|
57
|
+
|
|
58
|
+
- `<img src>`, `<video src>`, `<audio src>`, media `src`/`poster` — browsers ignore `javascript:` in media
|
|
59
|
+
- `<link href>` stylesheets
|
|
60
|
+
- Hardcoded paths
|
|
61
|
+
|
|
62
|
+
**Nesting:** sanitize first, then escape for HTML attributes. No escape needed for direct property assignment.
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// ✅ CORRECT — href in innerHTML (dynamic URL)
|
|
66
|
+
$el.innerHTML = `<a href="${webManager.utilities().escapeHTML(webManager.utilities().sanitizeURL(tab.url))}">${webManager.utilities().escapeHTML(tab.title)}</a>`;
|
|
67
|
+
|
|
68
|
+
// ✅ CORRECT — property assignment
|
|
69
|
+
$link.href = webManager.utilities().sanitizeURL(tab.url);
|
|
70
|
+
extension.tabs.create({ url: webManager.utilities().sanitizeURL(tab.url) });
|
|
71
|
+
|
|
72
|
+
// ❌ WRONG — escape alone lets `javascript:alert(1)` through
|
|
73
|
+
`<a href="${webManager.utilities().escapeHTML(tab.url)}">...</a>`
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Do NOT Escape Values Passed to textContent-Based APIs
|
|
77
|
+
|
|
78
|
+
`escapeHTML()` passes non-strings through unchanged — numbers, booleans, `null`, `undefined` come out exactly as they went in. Wrapping these is pure ceremony; it doesn't improve safety and adds reading cost. Don't escape:
|
|
79
|
+
|
|
80
|
+
- **Numbers, booleans, `null`, `undefined`** — `tabs.length`, `Date.now()`, flags.
|
|
81
|
+
- **`String(number)` coercion before escaping** — `escapeHTML(String(n))` is doubly redundant. Just interpolate: `${n}`.
|
|
82
|
+
- **Values from hardcoded maps/enums** — `STATUS_LABELS[key]`, icon-name literals, static SVG data URLs.
|
|
83
|
+
- **Framework-formatted strings** — `date.toLocaleDateString()`, `num.toFixed(2)`.
|
|
84
|
+
|
|
85
|
+
Safe by context (regardless of value):
|
|
86
|
+
|
|
87
|
+
- `textContent`, `.value`, `.placeholder` property assignments
|
|
88
|
+
- `setAttribute()` calls
|
|
89
|
+
- `webManager.utilities().showNotification(...)` — uses textContent internally; pre-escaping causes `'` → `'` double-encoding visible to users.
|
|
90
|
+
|
|
91
|
+
## See also
|
|
92
|
+
|
|
93
|
+
- [components.md](components.md) — component architecture (where these strings get rendered)
|
|
94
|
+
- [common-mistakes.md](common-mistakes.md) — the local-helper violation is mistake #1
|
|
95
|
+
- `web-manager/src/modules/utilities.js` — the `escapeHTML` / `sanitizeURL` implementations
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-extension-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Browser Extension Manager dependency manager",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"mgr": "bin/browser-extension-manager"
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
|
-
"dist"
|
|
37
|
+
"dist/",
|
|
38
|
+
"bin/",
|
|
39
|
+
"docs/",
|
|
40
|
+
"CLAUDE.md"
|
|
38
41
|
],
|
|
39
42
|
"preparePackage": {
|
|
40
43
|
"input": "./src",
|