Atlas / Skills / sucotasch / ext-dev-loop

ext-dev-loopBLOCK

skills/sucotasch/ext-dev-loop

Agent skill + zero-dependency CDP harness for the browser-extension dev loop: live SW console, scratch isolation, host auto-discovery/download

Verdict
BLOCK
Grade
D
Trust score
69 /100
Version
—
Hosts
1 documented
License
MIT
Stars
0
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

A skill + zero-dependency harness that closes the dev loop for Chrome/Edge (MV3) browser extensions: the agent launches a disposable browser, loads the unpacked extension, reads the service worker's console live, drives the extension's own pages, sends messages to the worker, and reproduces issues on real sites — without the user copying anything out of DevTools, and without ever touching the user's own browser sessions.

you:  "the extension is broken on this gallery site again"
agent: doctor → probe --headless --script steps.json → real SW console + real answers

What it does

  • `scripts/probe.mjs` — launch/reload/watch/live-SW-console/message/screenshot in one

invocation (--script batches N steps against one browser). Isolation is enforced: a scratch --user-data-dir under .ext-dev-loop/ roots is the only profile it will ever start, downloads are redirected out of the user's folders, shutdown goes through CDP Browser.close then the PID tree — never by image name.

  • `scripts/doctor.mjs` — will the loop work HERE, for THIS extension: hosts and their

measured ability to load an unpacked extension, scratch-root hygiene, the probe's own safety guards, every file the manifest names. --live adds one headless launch.

  • `scripts/get-browser.mjs` — find a usable host, or download Chrome for Testing

(official Google feed) into ~/.ext-dev-loop/browsers/ when no installed browser can host an unpacked extension.

  • `scripts/firefox.mjs` — a second engine (WebDriver BiDi): load the tree as a

temporary add-on, page-level measurements. Reached as probe.mjs --browser firefox.

  • `scripts/ports.mjs` / `instances.mjs` — lab hygiene: sweep/close leftover lab

browsers through their own debug endpoints; report live ports with no registry record.

Install (any agent harness that reads SKILL.md folders)

git clone https://github.com/Sucotasch/ext-dev-loop ~/.agents/skills/ext-dev-loop

then junct

Read from source at commit e7eae652d5eeOBSERVED · 2026-10-10
Advertisement
02

Install

Commands as the repository documents them. They are shown, not run.

git clone https://github.com/Sucotasch/ext-dev-loop ~/.agents/skills/ext-dev-loop
03

Host compatibility

What the documentation claims. We have not run a compatibility test.

HostStatusNotes
codexmentioned
04

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: ext-dev-loop
description: Use when developing a browser extension.
---

# ext-dev-loop

Closes the extension dev loop. Instead of the user reloading the extension, copying
SW console output and pasting errors back, the agent owns the cycle — launch, reload,
live service-worker logs, reproduce on a page — in a browser instance that cannot
touch the user's own sessions.

## Safety first: you are a guest on this machine

Three rules, all ENFORCED by `scripts/probe.mjs` rather than trusted to memory:

1. **Every launch carries `--user-data-dir` inside a `.ext-dev-loop` scratch root** —
   `./.ext-dev-loop/<ext>/profile` by default (findable, deletable, gitignored),
   `~/.ext-dev-loop/<name>/profile` when one profile should serve several projects.
   A `--profile` outside those roots is refused (exit 2) with the reason, before
   anything is spawned. Without this, a launch lands in the user's real, logged-in
   session.
2. **Never launch a browser to ask it something.** Measured 2026-09-19 on Windows:
   `msedge.exe --version` does not print a version — it *starts a browser*, with the
   default profile, i.e. the user's own Edge session. Read versions from the install
   directory instead (`versionOf()` in probe.mjs); measured: Edge `153.0.4234.32`,
   Chrome `141.0.7390.76`.
3. **Never kill by image name.** The user's own 100+ browser processes look exactly
   like the ones you started. Shutdown is CDP `Browser.close` on the instance's own
   debugging endpoint, with `taskkill /F /T /PID <spawned pid>` as fallback.

`--dry-run` prints host, version, profile and flags and exits BEFORE any spawn
(verified: no spawn on any path, refusals included) — use it as the cheap first step
and as the way to check the guards.

## Verified live

- 2026-09-19, Edge 153.0.4234.32, Windows, this project's extension: the unpacked
  extension loads with `--load-extension`, and the SW is found by evaluating
  `chrome.runtime.getManifest().name` per service_worker target ("SunoGrab - Suno
  Audio Downloader", v3.0.0) — among ~17 unrelated `msedge.exe` processes and several
  component-extension SWs.
- Chrome ≥137 is refused up front (Chrome 141 here): it ignores `--load-extension`.
  `--allow-chrome` overrides knowingly. The preferred host changed to **Chrome for Testing 137**
  after an Edge 154 update silently broke unpacked-extension loading — see fact 1.
- An idle MV3 SW produced **0 console lines in 70 s**. Nothing happens until work is
  triggered, so "I streamed the console" is not by itself evidence of anything.
- **An attached SW does not go idle.** With `Runtime.enable` attached it was still the
  same target after 70 s of silence (0 console lines, 0 exceptions), twice. This is the
  practical reason to attach first and ask questions later: the world you are watching
  is the world that stays alive.
- `/json/new?<url>` opens **both** a site page and an extension page:
  `chrome-extension://<id>/options.html` (HTTP 200, target created, and the page really
  rendered — title plus its 5 format radios). `Target.createTarget` was not needed.
- **A content script lives in an isolated world, and it is reachable.** On suno.com the
  page target exposed, next to several default contexts, one with
  `auxData.isDefault === false`, `type=isolated`, `origin=chrome-extension://<id>` and
  `name` = the extension's name. In THAT context `chrome.runtime.id` and the manifest's
  own globals exist (`typeof lamejs === "object"`, the vendored encoder); the very same
  expression in a default context answers `runtimeId: null`, `lamejs: "undefined"`.
  `--eval ... --in isolated` does this from the command line.
- **Console attribution has a catch.** `Runtime.consoleAPICalled` carries
  `executionContextId` (the older `context` field is absent in this build): a log issued
  in the isolated world arrived with the isolated context's id. But a page has SEVERAL
  default contexts (top frame, about:blank iframes), so match an id against the context
  list (`isDefault`/`origin`) instead of assuming one context per page.
- **The loop can drive the product.** From the options page target,
  `chrome.runtime.sendMessage({action:"ui:queueState"})` returned the SW's real summary
  — so a page you opened is not just readable, it can be used to make the extension do
  something and then read the answer.
- **Headless loads unpacked extensions.** `--headless=new` + `--load-extension` found the
  SW (same id, v3.0.0) and rendered extension pages normally — a visible window is needed
  only for what a human must do (a login). Measured with `doctor.mjs --live`.
- **Screenshots work headless too**, at the viewport size (`--window-size=1100,820` →
  a 1076×728 PNG, ~60 KB): `--shot` on `options.html` and on a heavier page (a DOM-built
  tab) both produced valid PNGs.
- The extension id an unpacked extension gets is **stable for the same path + profile**
  (`chrome-extension://<id>/...` kept working across runs), so pages can be addressed by
  URL in scripts once you have seen the id once.
- **A scratch profile is not an empty profile.** Edge installs registry/managed
  extensions into it (measured: a third-party security extension's content script was
  running on suno.com in a profile that had never seen a human, and its stderr noise
  `external_registry_loader_win.cc` appears on every launch). Consequences: identify the
  SW by manifest name, and pick the isolated world by YOUR extension's id — "the first
  non-default context" can be somebody else's. `probe.mjs --in isolated` does that, and
  says so when it sees a foreign world.
- A single-page app may render nothing 2.5 s in. `--delay <ms>` waits longer before the
  `--eval`/`--shot` step (measured need: an SPA's own content, not the extension's).
- **A content script (or page) cannot construct a Worker from `chrome-extension://`.**
  Measured on suno.com with a WAR-declared worker: `new Worker(extUrl)` and
  `new Worker(extUrl, {type:"module"})` both throw `cannot be accessed from
05

Trust audit

BLOCKgrade D · trust 69/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (1 observation(s))
Network
declared (6 observation(s))
Shell
declared (3 observation(s))
Dependencies
pinned
Secrets in source
none-found

Findings (7)

HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
scripts/probe.mjs:466
console.log(`  action:  ${MSG ? "--msg" : EVAL ? `--eval (--target ${TARGET})` : SHOT ? "--shot" : RELOAD ? "--reload" : WATCH_GIVEN ? "--watch" : "(state only)"}`);
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
scripts/probe.mjs:1141
if (!WATCH_GIVEN) { closeAll(); await sleep(80); reportStderr(); await stop("after --eval (sw)"); process.exit(process.exitCode || 0); }
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
SKILL.md:316
a scratch profile, poll `http://127.0.0.1:<port>/json/list` for `service_worker`
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
SKILL.md:597
"prove" a false defect). Open with `{"target":"page","url":"http://127.0.0.1:9811/index.html","delay":2500}`.
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
SKILL.md:732
`http://127.0.0.1:<port>/json/version` on 9793-9831 and nothing else. The Imagus project learned
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
scripts/instances.mjs:53
const v = await (await fetch(`http://127.0.0.1:${port}/json/version`, { signal: AbortSignal.timeout(900) })).json();
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
references/measurements.md:116
`http://127.0.0.1:<port>/json/version` on 9793-9831 and nothing else. The Imagus project learned

Gates applied: no_behavioural_pass.

Audited 2026-10-10 · audit v0.4.1 · source sha e7eae652d5eefull audit observations/trust-audit/skill/sucotasch__ext-dev-loop.json · Report an issue / request a re-scan
06

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-10e7eae652d5eeBLOCKD69first audit
07

Questions

What does the ext-dev-loop skill do?

Agent skill + zero-dependency CDP harness for the browser-extension dev loop: live SW console, scratch isolation, host auto-discovery/download

Is ext-dev-loop safe to install?

No — not without reading the findings first. The audit graded it D (69/100) and found 2 critical or high issues in the source. Each one is listed on this page with the file and line it is on.

What can ext-dev-loop access on my machine?

The audit observed that it reaches the network, runs shell commands and reads or writes files. Each of those is consistent with what it says it does. Secrets in the source: none found.

Which assistants does ext-dev-loop work with?

Its documentation mentions codex. That is what the text claims, not a compatibility test we ran.

How current is this page?

The grade is for one exact copy of the source (e7eae652d5ee), read on 2026-10-10. The repository is watched, and a new audit runs when it changes — this is the first audit.

Advertisement