feat!: harden raw browser control and packaging
Testing / remote-protocol-compat (0.9.3) (push) Successful in 40s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 38s
Testing / test (push) Failing after 1m3s
Package Extension / package-extension (push) Successful in 29s
Build & Publish Package / publish (push) Successful in 33s
Testing / remote-protocol-compat (0.9.3) (push) Successful in 40s
Testing / remote-protocol-compat (0.9.5) (push) Successful in 38s
Testing / test (push) Failing after 1m3s
Package Extension / package-extension (push) Successful in 29s
Build & Publish Package / publish (push) Successful in 33s
- Add safe-by-default policy gates for raw command surfaces: command, script, and serve-http /command. - Require explicit opt-ins for page reads, browser control, and high-risk commands such as dom.eval, storage.*, and screenshots. - Remove all cookies support from CLI, SDK, extension commands, permissions, constants, docs, and tests. - Add diagnostic, events, watch, workspace, remote, raw command, script, HTTP gateway, tree-view, session import/export, and extension info/capability commands. - Add Chrome Web Store packaging that strips manifest.key while keeping local packages with a stable native-messaging extension ID. - Bump browser-cli and extension version to 0.14.1 and cover the new behavior with pytest and extension packaging tests. BREAKING CHANGE: cookies commands and the b.cookies SDK namespace have been removed; generic raw command execution now blocks non-safe commands unless explicitly allowed.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package the Chrome extension.
|
||||
|
||||
Default builds a local/unpacked-style archive that keeps manifest.key so the
|
||||
extension ID stays stable for native messaging. ``--webstore`` writes the same
|
||||
runtime files but strips ``key`` from manifest.json because the Chrome Web Store
|
||||
rejects that field.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
EXTENSION_DIR = ROOT / "extension"
|
||||
DIST_DIR = ROOT / "dist"
|
||||
RUNTIME_FILES = (
|
||||
"manifest.json",
|
||||
"background.js",
|
||||
"content-dispatch.js",
|
||||
"content.js",
|
||||
"icon.svg",
|
||||
)
|
||||
RUNTIME_DIRS = ("icons",)
|
||||
|
||||
def _read_manifest(webstore: bool) -> dict:
|
||||
manifest = json.loads((EXTENSION_DIR / "manifest.json").read_text(encoding="utf-8"))
|
||||
if webstore:
|
||||
manifest.pop("key", None)
|
||||
return manifest
|
||||
|
||||
def _copy_tree(src: Path, dst: Path) -> None:
|
||||
if dst.exists():
|
||||
shutil.rmtree(dst)
|
||||
shutil.copytree(src, dst)
|
||||
|
||||
def package_extension(*, webstore: bool = False, out: Path | None = None) -> Path:
|
||||
manifest = _read_manifest(webstore)
|
||||
version = manifest["version"]
|
||||
suffix = "webstore" if webstore else "local"
|
||||
out = out or DIST_DIR / f"browser-cli-extension-{suffix}-v{version}.zip"
|
||||
staging = DIST_DIR / f"extension-package-{suffix}"
|
||||
|
||||
if staging.exists():
|
||||
shutil.rmtree(staging)
|
||||
staging.mkdir(parents=True)
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for file_name in RUNTIME_FILES:
|
||||
source = EXTENSION_DIR / file_name
|
||||
if file_name == "manifest.json":
|
||||
(staging / file_name).write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
else:
|
||||
shutil.copy2(source, staging / file_name)
|
||||
|
||||
for dir_name in RUNTIME_DIRS:
|
||||
_copy_tree(EXTENSION_DIR / dir_name, staging / dir_name)
|
||||
|
||||
if out.exists():
|
||||
out.unlink()
|
||||
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
||||
for path in sorted(staging.rglob("*")):
|
||||
if path.is_file():
|
||||
zf.write(path, path.relative_to(staging).as_posix())
|
||||
return out
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Package browser-cli extension")
|
||||
parser.add_argument("--webstore", action="store_true", help="strip manifest.key for Chrome Web Store upload")
|
||||
parser.add_argument("--out", type=Path, default=None, help="output zip path")
|
||||
args = parser.parse_args()
|
||||
print(package_extension(webstore=args.webstore, out=args.out))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user