mirror of
https://github.com/KingOfTheNOPs/CDP-Toolkit
synced 2026-06-29 08:59:48 +00:00
added extension loader
This commit is contained in:
@@ -22,6 +22,7 @@ cdptk bookmarks list --cdp-endpoint http://127.0.0.1:9222 --out bookmarks.json
|
||||
cdptk history search azure --cdp-endpoint http://127.0.0.1:9222 --limit 50
|
||||
cdptk saved-passwords list --cdp-endpoint http://127.0.0.1:9222
|
||||
cdptk extensions list --cdp-endpoint http://127.0.0.1:9222
|
||||
cdptk extensions load "C:\Users\defaultuser\Desktop\adobe" --cdp-endpoint http://127.0.0.1:9222
|
||||
```
|
||||
|
||||
## Features
|
||||
@@ -133,6 +134,17 @@ The output can include names, extension IDs, enabled state, descriptions, views/
|
||||
cdptk extensions list --cdp-endpoint http://127.0.0.1:9222 --out extensions.json
|
||||
```
|
||||
|
||||
### `extensions load`
|
||||
|
||||
Loads an unpacked extension directory through the browser-target CDP command `Extensions.loadUnpacked`. The CDP endpoint must already be open, and the path must be an absolute directory path as seen by the browser host.
|
||||
|
||||
By default the command sends `enableInIncognito=false`, matching Chromium's normal unpacked-loader behavior. Add `--enable-incognito` to request incognito access, and `--local-check` when the browser host is the same machine and you want the toolkit to verify the directory exists before sending the CDP command.
|
||||
|
||||
```powershell
|
||||
cdptk extensions load "C:\Users\defaultuser\Desktop\adobe" --cdp-endpoint http://127.0.0.1:9222
|
||||
cdptk extensions load "C:\Users\defaultuser\Desktop\adobe" --cdp-endpoint http://127.0.0.1:9222 --out loaded-extension.json
|
||||
```
|
||||
|
||||
### `page new`
|
||||
|
||||
Creates a new browser target through CDP. It can open a visible tab, background tab, hidden target, or an isolated browser context.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[
|
||||
{
|
||||
"type": "bookmark",
|
||||
"id": "5",
|
||||
"parentId": "2",
|
||||
"title": "Dashboard | Hack The Box Academy",
|
||||
"url": "https://academy.hackthebox.com/app/dashboard",
|
||||
"path": "Other bookmarks",
|
||||
"dateAdded": 1777945660424,
|
||||
"dateGroupModified": null,
|
||||
"source": "bookmarks-api"
|
||||
}
|
||||
]
|
||||
+31
-1
@@ -39,7 +39,7 @@ app.add_typer(cookies_app, name="cookies", help="Collect and export browser cook
|
||||
app.add_typer(bookmarks_app, name="bookmarks", help="Collect browser bookmarks/favorites through CDP-rendered WebUI.")
|
||||
app.add_typer(history_app, name="history", help="Search browser history through CDP-rendered browser WebUI.")
|
||||
app.add_typer(saved_passwords_app, name="saved-passwords", help="List saved-password sites and dump autofill-backed entries through CDP.")
|
||||
app.add_typer(extensions_app, name="extensions", help="Inventory extensions through CDP-rendered browser WebUI.")
|
||||
app.add_typer(extensions_app, name="extensions", help="Inventory and load extensions through CDP.")
|
||||
app.add_typer(contexts_app, name="contexts", help="List and dispose non-default browser contexts.")
|
||||
app.add_typer(browser_takeover_app, name="browser-takeover", help="Browse through the CDP-attached browser using screencast or proxy mode.")
|
||||
|
||||
@@ -1014,3 +1014,33 @@ def extensions_list(
|
||||
data = run(_run())
|
||||
dump_json(data, out)
|
||||
console.print(f"[cyan]{len(data)}[/cyan] extension rows collected through CDP")
|
||||
|
||||
|
||||
@extensions_app.command("load")
|
||||
def extensions_load(
|
||||
extension_path: str = typer.Argument(..., help="Absolute unpacked extension directory path on the browser host."),
|
||||
cdp_endpoint: str = REQUIRED_CDP_ENDPOINT,
|
||||
socks: Optional[str] = typer.Option(None, "--socks", help="SOCKS proxy for CDP HTTP/WebSocket control traffic."),
|
||||
enable_incognito: bool = typer.Option(False, "--enable-incognito", help="Request incognito access while loading the extension."),
|
||||
local_check: bool = typer.Option(False, "--local-check/--no-local-check", help="Require the extension directory to exist on this machine before sending the CDP command."),
|
||||
out: Optional[Path] = typer.Option(None, "--out", "-o"),
|
||||
) -> None:
|
||||
"""Load an unpacked extension through Extensions.loadUnpacked."""
|
||||
|
||||
if local_check and not Path(extension_path).is_dir():
|
||||
raise typer.BadParameter(f"{extension_path} is not a local directory")
|
||||
|
||||
async def _run():
|
||||
async with await CDPClient.connect(cdp_endpoint, socks) as cdp:
|
||||
result = await cdp.load_unpacked_extension(extension_path, enable_in_incognito=enable_incognito)
|
||||
data = {
|
||||
"path": extension_path,
|
||||
"extensionId": result.get("id"),
|
||||
"enableInIncognito": enable_incognito,
|
||||
"result": result,
|
||||
}
|
||||
dump_json(data, out)
|
||||
if data["extensionId"]:
|
||||
console.print(f"[green]loaded[/green] extension {data['extensionId']}")
|
||||
|
||||
run(_run())
|
||||
|
||||
@@ -77,6 +77,12 @@ class CDPClient:
|
||||
info("attached session {}", result["sessionId"])
|
||||
return TargetSession(self, target_id, result["sessionId"])
|
||||
|
||||
async def attach_browser_target(self) -> TargetSession:
|
||||
info("attaching to browser target")
|
||||
result = await self.send("Target.attachToBrowserTarget")
|
||||
info("attached browser session {}", result["sessionId"])
|
||||
return TargetSession(self, "browser", result["sessionId"])
|
||||
|
||||
async def detach(self, session: TargetSession) -> None:
|
||||
info("detaching session {}", session.session_id)
|
||||
await self.send("Target.detachFromTarget", {"sessionId": session.session_id})
|
||||
@@ -144,6 +150,17 @@ class CDPClient:
|
||||
info("setting {} cookies{}", len(cookies), f" in context {browser_context_id}" if browser_context_id else "")
|
||||
await self.send("Storage.setCookies", params)
|
||||
|
||||
async def load_unpacked_extension(self, path: str, *, enable_in_incognito: bool = False) -> dict:
|
||||
session = await self.attach_browser_target()
|
||||
try:
|
||||
info("loading unpacked extension from {}", path)
|
||||
return await session.send(
|
||||
"Extensions.loadUnpacked",
|
||||
{"path": path, "enableInIncognito": enable_in_incognito},
|
||||
)
|
||||
finally:
|
||||
await self.detach(session)
|
||||
|
||||
async def screenshot(self, target_id: str, out: Path, *, full: bool = False) -> Path:
|
||||
session = await self.attach(target_id)
|
||||
try:
|
||||
|
||||
+3228
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user