From 3fa70b1df3ec62cdb9675397bfc369e0548e7f3e Mon Sep 17 00:00:00 2001 From: andwati Date: Wed, 12 Aug 2026 16:03:13 +0300 Subject: [PATCH] Make the Chrome Web Store ID the single canonical extension ID The extension is now published (ID: mkiffclapimhbjjlgkndlillglopbcic). Drop the pinned "key" field from manifest.json (the Store rejects it anyway) and use the Store-assigned ID everywhere instead of juggling two IDs: native-host/com.kget_capture.host.json's allowed_origins, keys/extension-id.txt, and docs. This also removes the need for a separate key-stripped release zip, since there's no key left to strip. --- .github/workflows/release.yml | 29 +++------------ README.md | 25 ++++++------- extension/manifest.json | 1 - install.sh | 12 ++++--- keys/README.md | 50 ++++++++++++-------------- keys/extension-id.txt | 2 +- native-host/com.kget_capture.host.json | 2 +- 7 files changed, 49 insertions(+), 72 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7f5f412..0431ab6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,34 +18,14 @@ jobs: - name: Package extension # Chrome requires manifest.json at the zip root, both for Web Store # upload and for manually loading unpacked -- so this one archives - # only extension/'s contents, not the whole repo. Keeps the pinned - # "key" field, so unpacked loads get the stable, predictable ID. + # only extension/'s contents, not the whole repo. No "key" field to + # worry about: the Web Store's own assigned ID is now the single + # canonical one (see keys/README.md), so this same zip works for + # both a Store upload and a manual unpacked install. run: | cd extension zip -r "../kget-capture-extension-${GITHUB_REF_NAME}.zip" . - - name: Package extension for Web Store upload - # The Chrome Web Store dashboard rejects any manifest containing a - # "key" field ("key field is not allowed in manifest") -- it assigns - # its own permanent ID on first publish instead. Strip it from a - # copy so this zip is upload-ready; the other extension zip above - # keeps "key" for stable unpacked/dev IDs. - run: | - rm -rf /tmp/store-extension - cp -r extension /tmp/store-extension - python3 -c " - import json - path = '/tmp/store-extension/manifest.json' - with open(path) as f: - manifest = json.load(f) - manifest.pop('key', None) - with open(path, 'w') as f: - json.dump(manifest, f, indent=2) - f.write('\n') - " - cd /tmp/store-extension - zip -r "$OLDPWD/kget-capture-store-${GITHUB_REF_NAME}.zip" . - - name: Package source # Separately, the native-host installer bundle (install.sh, # native-host/, keys/) needs the whole repo. git archive only @@ -59,5 +39,4 @@ jobs: files: | kget-capture-${{ github.ref_name }}.zip kget-capture-extension-${{ github.ref_name }}.zip - kget-capture-store-${{ github.ref_name }}.zip generate_release_notes: true diff --git a/README.md b/README.md index dbb2f00..5e58826 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,10 @@ per step 2 above.) Then in `chrome://extensions`: enable **Developer mode**, click **Load unpacked**, and select the `extension/` directory (from your clone, or the -extracted extension zip). `install.sh` prints the extension's fixed ID so -you can confirm it loaded correctly. +extracted extension zip). Chrome assigns this unpacked copy its own ID, +different from the published extension's, so add it to `allowed_origins` in +`~/.config//NativeMessagingHosts/com.kget_capture.host.json` (see +[keys/README.md](keys/README.md)) for the native host to accept it. ## Usage @@ -115,19 +117,18 @@ Store listing); that step can't be scripted. Prebuilt releases are published on the [Releases page](https://github.com/andwati/kget-capture/releases); each -`vX.Y.Z` tag push builds and publishes one automatically, as three zips: +`vX.Y.Z` tag push builds and publishes one automatically, as two zips: - `kget-capture-vX.Y.Z.zip`: full source, for `install.sh`/`install-remote.sh`. - `kget-capture-extension-vX.Y.Z.zip`: just the extension, manifest at the - zip root, `key` field intact, for manual "Load unpacked" installs. -- `kget-capture-store-vX.Y.Z.zip`: same extension, but with `key` stripped, - for uploading to the Chrome Web Store dashboard. The Store rejects any - manifest containing `key` ("key field is not allowed in manifest") since - it assigns its own permanent ID on first publish, which won't match the - pinned ID (`keys/extension-id.txt`) the other zips use. After the first - successful Store publish, update `native-host/com.kget_capture.host.json`'s - `allowed_origins` to include the Store-assigned ID alongside the pinned - one, so both installs can reach the native host. + zip root, ready for either a manual "Load unpacked" install or a Chrome + Web Store upload. + +The published extension's canonical ID +(`mkiffclapimhbjjlgkndlillglopbcic`) is hardcoded into +`native-host/com.kget_capture.host.json`'s `allowed_origins`; see +[keys/README.md](keys/README.md) for why, and what to do for local unpacked +testing (which gets a different, machine-specific ID). ## Known limitations diff --git a/extension/manifest.json b/extension/manifest.json index ecd03d3..749892a 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -3,7 +3,6 @@ "name": "KGet Capture", "version": "1.0.6", "description": "Send browser downloads to KGet (KDE Download Manager) on Linux.", - "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtM1nFg5pPd3UBzynUJHj6aUy7bjudZaX9AQTWGMqAX3fXylw1lubSzP1/BaS9ivvgOJg/loyEgzEZlcNPzblFVlg8BqEJ3tntdk2OzYvUs0pUdcPzzyjso9TykCkUUTI7ev8JkreW/FYlinfvNwSYN4/c1s3h8/I6XHtxOdmO1G3bS98+BQaXPldbDmxoKwyWR8u1f8gTO2Mg3GY4BHau3RWf1kgtNzh20p9wE3sEFdWkFu7ArgRCIrWLh6m3O6lolnHQSCWPwtOTx893Qkp9Eu6FwUnoWc8rsYx8sqa0eNCTCS6Kjr1Iu3yEJ5y9390jUiDZTQ/c62ueCTQaYv2bwIDAQAB", "permissions": ["downloads", "contextMenus", "storage", "nativeMessaging"], "background": { "service_worker": "background.js" diff --git a/install.sh b/install.sh index 98f401c..67f0f59 100755 --- a/install.sh +++ b/install.sh @@ -129,19 +129,21 @@ if ! command -v notify-send >/dev/null 2>&1; then echo "WARNING: 'notify-send' not found -- captures will still work, but you won't get a notification when they finish" fi -EXT_ID=$(cat "$SCRIPT_DIR/keys/extension-id.txt") - cat </NativeMessagingHosts/com.kget_capture.host.json + so the native host will accept connections from it too. Make sure KGet is running (or set to autostart) for captures to work. EOF diff --git a/keys/README.md b/keys/README.md index b6ef834..d2afc78 100644 --- a/keys/README.md +++ b/keys/README.md @@ -1,32 +1,28 @@ -# Extension signing key +# Extension ID -This directory holds the RSA key pair used to pin the extension's Chrome ID so -it's identical across every install (needed so the native messaging host's -`allowed_origins` can be hardcoded instead of asking each user to paste their -extension ID). +`extension-id.txt` holds the extension's one canonical ID, +`mkiffclapimhbjjlgkndlillglopbcic`, assigned by the Chrome Web Store on +first publish. It's hardcoded into +`native-host/com.kget_capture.host.json`'s `allowed_origins`, which is what +lets the native host trust connections from the extension without asking +each user to paste their own ID. -- `kget-capture-private.pem` — **not committed** (see `.gitignore`). Keep it - somewhere safe if you want to be able to reproduce the same key deterministically - later; losing it just means a future re-key would get a new extension ID, it's - not a security incident. -- `kget-capture-public.der` / `kget-capture-public.b64` — derived from the - private key. The base64 contents of `kget-capture-public.b64` is pasted - verbatim into `extension/manifest.json`'s `"key"` field. These are already - effectively public once checked into `manifest.json`, so committing them is - fine (and `extension-id.txt` too). -- `extension-id.txt` — the 32-character Chrome extension ID derived from the - public key (also hardcoded into `native-host/com.kget_capture.host.json`). +`extension/manifest.json` intentionally has no `"key"` field: the Chrome Web +Store dashboard rejects any manifest that includes one ("key field is not +allowed in manifest"), since it assigns its own permanent ID on first +publish instead. -## Regenerating (only if rotating the key) +`kget-capture-private.pem` / `kget-capture-public.der` / `kget-capture-public.b64` +are leftover from an earlier approach that pinned a separate ID for unpacked +installs via a local key pair. They're no longer referenced anywhere (the +`.pem` was never committed; see `.gitignore`) and can be ignored or removed. -```bash -openssl genrsa -out keys/kget-capture-private.pem 2048 -openssl rsa -in keys/kget-capture-private.pem -pubout -outform DER -out keys/kget-capture-public.der -openssl base64 -A -in keys/kget-capture-public.der -out keys/kget-capture-public.b64 -openssl dgst -sha256 -binary keys/kget-capture-public.der | head -c 16 | od -An -tx1 \ - | tr -d ' \n' | tr '0123456789abcdef' 'abcdefghijklmnop' > keys/extension-id.txt -``` +## Implication for local "Load unpacked" development -Rotating the key changes the extension ID, which means `extension/manifest.json`'s -`"key"` and `native-host/com.kget_capture.host.json`'s `allowed_origins` both -need updating to match. +Since there's no pinned key, Chrome derives an unpacked install's ID from a +hash of its local path, which won't match +`mkiffclapimhbjjlgkndlillglopbcic` and differs per machine/checkout. To test +an unpacked build against the native host locally, add your own unpacked +ID to `allowed_origins` in `~/.config//NativeMessagingHosts/com.kget_capture.host.json` +after running `install.sh` (Chrome shows the unpacked ID on +`chrome://extensions` once loaded). diff --git a/keys/extension-id.txt b/keys/extension-id.txt index 1be9663..53ee865 100644 --- a/keys/extension-id.txt +++ b/keys/extension-id.txt @@ -1 +1 @@ -lgkhlfhejbffjnpcdlpjclicihnbjijp +mkiffclapimhbjjlgkndlillglopbcic \ No newline at end of file diff --git a/native-host/com.kget_capture.host.json b/native-host/com.kget_capture.host.json index fd87681..c8592bc 100644 --- a/native-host/com.kget_capture.host.json +++ b/native-host/com.kget_capture.host.json @@ -3,5 +3,5 @@ "description": "Native messaging host that hands off download URLs to KGet via D-Bus", "path": "__HOST_SCRIPT_PATH__", "type": "stdio", - "allowed_origins": ["chrome-extension://lgkhlfhejbffjnpcdlpjclicihnbjijp/"] + "allowed_origins": ["chrome-extension://mkiffclapimhbjjlgkndlillglopbcic/"] }