Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1bf44c0eef
|
|||
|
cf0c9555d0
|
|||
|
a7da6cfab0
|
|||
|
88b4f5ed11
|
|||
|
36abde501c
|
|||
|
1aff084429
|
+4
-4
@@ -36,6 +36,7 @@ from browser_cli.platform import install_base_dir, is_windows
|
||||
console = Console()
|
||||
|
||||
NATIVE_HOST_NAME = "com.browsercli.host"
|
||||
EXTENSION_ID = "bfpmkhngkjnfhabmfckgeohlilokodkg"
|
||||
|
||||
NATIVE_HOST_DIRS = {
|
||||
"chrome": {
|
||||
@@ -284,7 +285,7 @@ def cmd_install(browser):
|
||||
wrapper_content = f'@echo off\r\n"{sys.executable}" "{native_host_script_path}" %*\r\n'
|
||||
wrapper_path.write_text(wrapper_content, encoding="utf-8")
|
||||
|
||||
# Ask for extension ID
|
||||
# Load extension
|
||||
ext_urls = {
|
||||
"chrome": "chrome://extensions",
|
||||
"chromium": "chrome://extensions",
|
||||
@@ -297,10 +298,9 @@ def cmd_install(browser):
|
||||
console.print(f" 1. Open [cyan]{ext_url}[/cyan]")
|
||||
console.print(" 2. Enable [bold]Developer mode[/bold] (top-right toggle)")
|
||||
console.print(f" 3. Click [bold]Load unpacked[/bold] → select: [cyan]{Path(__file__).parent.parent / 'extension'}[/cyan]")
|
||||
console.print(" 4. Copy the [bold]Extension ID[/bold] shown on the extension card\n")
|
||||
console.print(f" 4. Extension ID will be [cyan]{EXTENSION_ID}[/cyan] (fixed by built-in key)\n")
|
||||
|
||||
extension_id = click.prompt("Paste your extension ID here")
|
||||
extension_id = extension_id.strip()
|
||||
extension_id = EXTENSION_ID
|
||||
|
||||
# Build native messaging manifest
|
||||
manifest = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "browser-cli",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Control your browser from the terminal via browser-cli",
|
||||
"permissions": [
|
||||
"tabs",
|
||||
@@ -13,7 +13,9 @@
|
||||
"nativeMessaging",
|
||||
"cookies"
|
||||
],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
@@ -29,5 +31,6 @@
|
||||
"16": "icons/icon-16.png",
|
||||
"32": "icons/icon-32.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlfCvygCocGbU2Bm2Rg6cnvHN0Lt25gJGJ/XX7VuAccrp4dH+Whj3Fw2vYSjgx90wuWuMl5fsWSsSX9H1k1vp7ImGzszCDnScn+o+KRWrVCQVRD1NEaKavuHoaHyc3Hs+njrM8c7c6u2ygdItZkggwPU0U1dKkixP/DWR9oG13Gr4u39p/xHxITiBh0DROYdoKBzw/J+vT7zWITKyG7QBgLMuoaYc15oqRIm7raBW1GIn1A5V2WPpBM9rMAli4vCyc9rbqsUqO1Yu4SrNIoG+wfz3MED3ajylDH6Jh1bsf1l5EZNDR/EpqBsSQcEV0VXX7nkqchqgzh3bgT9psiUQAQIDAQAB"
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "browser-cli"
|
||||
version = "0.6.0"
|
||||
version = "0.7.1"
|
||||
description = "Control your real running browser from the terminal via a browser extension"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env -S uv run
|
||||
"""Generate or derive Chrome extension key and ID.
|
||||
|
||||
Usage:
|
||||
python scripts/gen_extension_key.py # generate new key pair
|
||||
python scripts/gen_extension_key.py --from-manifest # derive ID from extension/manifest.json
|
||||
python scripts/gen_extension_key.py --key <base64> # derive ID from given public key
|
||||
"""
|
||||
import argparse, hashlib
|
||||
import base64, json, sys
|
||||
from pathlib import Path
|
||||
|
||||
def public_key_to_extension_id(pub_key_der:bytes) -> str:
|
||||
digest = hashlib.sha256(pub_key_der).hexdigest()
|
||||
return "".join(chr(ord("a") + int(c, 16)) for c in digest[:32])
|
||||
|
||||
def derive_from_key_b64(key_b64:str) -> tuple[str, str]:
|
||||
der = base64.b64decode(key_b64)
|
||||
ext_id = public_key_to_extension_id(der)
|
||||
return key_b64, ext_id
|
||||
|
||||
def generate_new_key() -> tuple[str, str, str]:
|
||||
try:
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives.serialization import (
|
||||
Encoding,
|
||||
PublicFormat,
|
||||
PrivateFormat,
|
||||
NoEncryption,
|
||||
)
|
||||
except ImportError:
|
||||
print("Install 'cryptography' to generate new keys: pip install cryptography", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
||||
pub_der = private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo)
|
||||
priv_pem = private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()).decode()
|
||||
|
||||
key_b64 = base64.b64encode(pub_der).decode()
|
||||
ext_id = public_key_to_extension_id(pub_der)
|
||||
return key_b64, ext_id, priv_pem
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Chrome extension key/ID tool")
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument("--from-manifest", action="store_true", help="Derive ID from extension/manifest.json")
|
||||
group.add_argument("--key", metavar="BASE64", help="Derive ID from given base64 public key")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.from_manifest:
|
||||
manifest_path = Path(__file__).parent.parent / "extension" / "manifest.json"
|
||||
manifest = json.loads(manifest_path.read_text())
|
||||
key_b64 = manifest.get("key")
|
||||
if not key_b64:
|
||||
print("No 'key' field in manifest.json", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
key_b64, ext_id = derive_from_key_b64(key_b64)
|
||||
print(f"Extension ID: {ext_id}")
|
||||
print(f"Key (b64): {key_b64}")
|
||||
|
||||
elif args.key:
|
||||
key_b64, ext_id = derive_from_key_b64(args.key)
|
||||
print(f"Extension ID: {ext_id}")
|
||||
|
||||
else:
|
||||
key_b64, ext_id, priv_pem = generate_new_key()
|
||||
print(f"Extension ID: {ext_id}")
|
||||
print(f"Key (b64): {key_b64}")
|
||||
print()
|
||||
print("Add this to extension/manifest.json:")
|
||||
print(f' "key": "{key_b64}"')
|
||||
print()
|
||||
print("Private key (keep secret, needed to re-derive same ID):")
|
||||
print(priv_pem)
|
||||
Reference in New Issue
Block a user