From 14ffd304175eeb4e2bbaeba983c9b157b0bb12ec Mon Sep 17 00:00:00 2001 From: leeguooooo Date: Wed, 10 Jun 2026 13:34:45 +0900 Subject: [PATCH] fix(extension): strip manifest "key" from the Web Store upload zip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Chrome Web Store rejects uploads whose manifest contains a "key" field ("manifest must not contain 'key'") — it assigns its own id. pack-extension.sh intentionally kept "key" in the zip, so every upload failed. Now the script stages a copy and removes "key" for the zip only; the unpacked DIR and the signed .crx keep "key" so local Load-unpacked + managed force-install stay pinned to ciiljdlhd…. After the first store upload, add the store-assigned id to the native-messaging allowed_origins (connect.rs EXTENSION_ID) so the store build pairs. --- extensions/ab-connect.zip | Bin 29937 -> 29594 bytes scripts/pack-extension.sh | 40 ++++++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/extensions/ab-connect.zip b/extensions/ab-connect.zip index 8bbf4875fe659aab59520a2bba1509e264324c17..45c2a58ee40b7fc3a91910e9058e294a951d8e93 100644 GIT binary patch delta 859 zcmezPl5y5^MxFp~W)=|!1_llWpPY$2@@xSwHL?O;ZZw?C%;b|Z`53bbRAlo9<~%`= zP=cr;OsHM7i`B^|=Tr<(KOC?z$S`Ck=jRpchlX%6FoVns1me;PZU#n{uZ#=~VC#^r zog6H!4l}YrdKweR7700+(B#vaDw{LqjJQFlF4OhZvAmCnJ&=L>Yx zOy5r;{y^*IGEFwfm8b`~0gF3YgHGlha^TtfSv2h2gh>L5N2WUK=hiP+dqKr8@l?(% zRSv~}d*>y3zF0ba;@kA!wzrGtF{Dh=+~@p!yKU6*t*5SSnBnGN*!d)7jq}s=tykUK z%bwX7J^UW&CF!-PGVbY7(_4$Ob@?WtebzFh7?)IH@nB^wkz5!thZt6QhWVTc5bq){EE5Pr(LTopP3M5m(>?hzhPc^re@%oa-rbO&n^TOU*$W! z(D}=rgKqz_n~~-?~mr^8=o?}#Ui|U z^2GOhMLLs;X8vJ9j@VN%^Ve)ojs`}8lnfIC11B(L`1yx;I=kxSroa;iFoA5gC@5zH z#pT9AU8aDSlOGhyf_VQ6jgWXjMfwn)I0qLvu>n;qD$+($@vg`eLxoE*rr4}vGYoYf tVd`Yj6Hjo78dD(HP0YZgK6!SDwCK7#24s)0f>ZhA(B%qAnoy`r>)0mimLK1Q?p~rmZ8{|sVy8?ak1L)Aw3T_5QmamKq3}BD8 z2KyE-auC@2JUk%IS442a6Sgd&MF;&&(#o|>4_GLjX)tDPS@P+A`2m$#d*8IadUstu z|Hbw9;*+i%XlRK(@xz->lUw-Gj)}Xbi#jm09m-^2;oSczBgnvGy+pghv)yiUi?lLN z=x)7gS?|VtqDE@jA7M#ny}vIMSxwF?3{jJu`iMnQBBs^l)%%R5g24GD5@Z&7u$Qc1_SN zHrr!**7N=x$+Xt*7d>t`2cJ(bJNd`Dde=-l_U@7>pW{XK!EWo{)dZajv_D!|Z|G6; z_~g-7jPc*Y?sf32d%O33XLy&8_3L!IHWG8-7oTP{c`D^?Z;Qe8awq@0IQck zK6~`?-HEywdt3IzEWx9XUEaFS=G^Fg{?Y-j`20;w7v7dEuG;=K zq343gBQ>34=Ps45S+ijM?`mcKb>P|_+rm?(`%n!Gtc~PzUgf?&zbr+jh4o1 z9|t*Y)KI(jNl)fs*_SNW(hae-)>Yc7EBr(=!hd#_O)U;Fty{FJu&-=NeS+YMLdN>`w0n$hnXgiIYp)Z7di51 z{9C8F;CX|M%hn)mqxvi(xev-JvnD_A|5ALU^vOd0_^uYWri*7Q!?Noag)aM=`AFqN zQbm>g2mAhae_v-tPdNIgwcY05dENP`%ROf;4K2&vQYLf!Ye#o~_kKj)3~xL+d3T;X zd)XfKtnxkTn;+(tGcxIAPmU_kWio#b%w4je+`6K`i0QyHFb|kfe;4R8-2w5$Ik>>N zb5Yo2??P=RaBh?ZWu)narc6noya`hQ6nl$B%(=)6L*1+*s9st0y!WR_jmZ`4CS#zp zW9F~fo*WI#d{V%i$;luAb_tNnu%wZ-fPul!Kg847RWCOsG=!Cb8R)&qCB+JCiwi-@ E0Oa8M`Tzg` diff --git a/scripts/pack-extension.sh b/scripts/pack-extension.sh index 3c08abc..06fecb2 100755 --- a/scripts/pack-extension.sh +++ b/scripts/pack-extension.sh @@ -1,11 +1,16 @@ #!/bin/sh # Build the Chrome Web Store upload package extensions/ab-connect.zip (and a signed -# extensions/ab-connect.crx for reference) from extensions/ab-connect, keeping the -# extension id constant via the stable signing key + manifest "key". +# extensions/ab-connect.crx for reference) from extensions/ab-connect. # -# The id MUST stay ciiljdlhdpfckdcfkphgmfalanpdejep so the native-messaging -# allowed_origins and the force-install policy keep matching. The id is pinned by -# the "key" field in manifest.json (kept in the uploaded zip on purpose). +# IMPORTANT — the "key" field: +# * The unpacked DIR (Load-unpacked) and the signed .crx KEEP the manifest "key", +# which pins the id to ciiljdlhdpfckdcfkphgmfalanpdejep so the native-messaging +# allowed_origins + managed force-install policy keep matching for local/dev use. +# * The Web Store UPLOAD zip MUST NOT contain "key" — the store rejects it +# ("manifest must not contain 'key'") and assigns its own id. So this script +# strips "key" from the manifest inside the zip only. After the first upload, +# note the store-assigned id and add it to the native-messaging allowed_origins +# (cli/src/connect.rs EXTENSION_ID) so the store build can pair too. # # The private key lives at .secrets/ab-connect.pem and is git-ignored. # @@ -20,20 +25,35 @@ KEY=.secrets/ab-connect.pem EXT=extensions/ab-connect CHROME="${CHROME_BIN:-/Applications/Google Chrome.app/Contents/MacOS/Google Chrome}" -# Web Store upload package (zip of the unpacked extension, dotfiles excluded). +# Web Store upload package: stage a copy with the "key" field removed, then zip. +STAGE=$(mktemp -d) +trap 'rm -rf "$STAGE"' EXIT +cp -R "$EXT/." "$STAGE/" +python3 - "$STAGE/manifest.json" <<'PY' +import json, sys +p = sys.argv[1] +m = json.load(open(p)) +m.pop("key", None) # the Web Store forbids the "key" field in uploads +json.dump(m, open(p, "w"), indent=2) +open(p, "a").write("\n") +PY rm -f extensions/ab-connect.zip -( cd "$EXT" && zip -rq ../ab-connect.zip . -x '.*' ) +( cd "$STAGE" && zip -rq "$OLDPWD/extensions/ab-connect.zip" . -x '.*' ) [ -f extensions/ab-connect.zip ] || { echo "error: zip failed" >&2; exit 1; } +if unzip -p extensions/ab-connect.zip manifest.json | grep -q '"key"'; then + echo "error: 'key' still present in upload zip" >&2; exit 1 +fi +echo "packed extensions/ab-connect.zip (key stripped for Web Store)" -# Signed crx (reference / non-store force-install for managed setups). +# Signed crx (reference / non-store force-install for managed setups) — keeps "key" +# via the signing key so the id stays ciiljdlhdpfckdcfkphgmfalanpdejep. if [ -f "$KEY" ]; then rm -f extensions/ab-connect.crx "$CHROME" --pack-extension="$PWD/$EXT" --pack-extension-key="$PWD/$KEY" >/dev/null 2>&1 || true ID=$(openssl rsa -in "$KEY" -pubout -outform DER 2>/dev/null \ | openssl dgst -sha256 -binary | xxd -p -c256 | head -c32 | tr '0-9a-f' 'a-p') - echo "extension id: $ID" + echo "local/crx extension id: $ID" else echo "note: $KEY missing — built zip only (no crx)." fi -echo "packed extensions/ab-connect.zip" echo "manifest version: $(grep -o '"version"[^,]*' "$EXT/manifest.json" | head -1)"