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()
|
console = Console()
|
||||||
|
|
||||||
NATIVE_HOST_NAME = "com.browsercli.host"
|
NATIVE_HOST_NAME = "com.browsercli.host"
|
||||||
|
EXTENSION_ID = "bfpmkhngkjnfhabmfckgeohlilokodkg"
|
||||||
|
|
||||||
NATIVE_HOST_DIRS = {
|
NATIVE_HOST_DIRS = {
|
||||||
"chrome": {
|
"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_content = f'@echo off\r\n"{sys.executable}" "{native_host_script_path}" %*\r\n'
|
||||||
wrapper_path.write_text(wrapper_content, encoding="utf-8")
|
wrapper_path.write_text(wrapper_content, encoding="utf-8")
|
||||||
|
|
||||||
# Ask for extension ID
|
# Load extension
|
||||||
ext_urls = {
|
ext_urls = {
|
||||||
"chrome": "chrome://extensions",
|
"chrome": "chrome://extensions",
|
||||||
"chromium": "chrome://extensions",
|
"chromium": "chrome://extensions",
|
||||||
@@ -297,10 +298,9 @@ def cmd_install(browser):
|
|||||||
console.print(f" 1. Open [cyan]{ext_url}[/cyan]")
|
console.print(f" 1. Open [cyan]{ext_url}[/cyan]")
|
||||||
console.print(" 2. Enable [bold]Developer mode[/bold] (top-right toggle)")
|
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(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
|
||||||
extension_id = extension_id.strip()
|
|
||||||
|
|
||||||
# Build native messaging manifest
|
# Build native messaging manifest
|
||||||
manifest = {
|
manifest = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "browser-cli",
|
"name": "browser-cli",
|
||||||
"version": "0.6.0",
|
"version": "0.7.1",
|
||||||
"description": "Control your browser from the terminal via browser-cli",
|
"description": "Control your browser from the terminal via browser-cli",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"tabs",
|
"tabs",
|
||||||
@@ -13,7 +13,9 @@
|
|||||||
"nativeMessaging",
|
"nativeMessaging",
|
||||||
"cookies"
|
"cookies"
|
||||||
],
|
],
|
||||||
"host_permissions": ["<all_urls>"],
|
"host_permissions": [
|
||||||
|
"<all_urls>"
|
||||||
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.js"
|
"service_worker": "background.js"
|
||||||
},
|
},
|
||||||
@@ -29,5 +31,6 @@
|
|||||||
"16": "icons/icon-16.png",
|
"16": "icons/icon-16.png",
|
||||||
"32": "icons/icon-32.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]
|
[project]
|
||||||
name = "browser-cli"
|
name = "browser-cli"
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
description = "Control your real running browser from the terminal via a browser extension"
|
description = "Control your real running browser from the terminal via a browser extension"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
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)
|
||||||
@@ -4,7 +4,7 @@ requires-python = ">=3.10"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "browser-cli"
|
name = "browser-cli"
|
||||||
version = "0.5.12"
|
version = "0.7.1"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
|
|||||||
Reference in New Issue
Block a user