protocol-browser-anti-stall — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited protocol-browser-anti-stall (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Apply these rules to EVERY browser automation action. No exceptions.
Also read `references/playwright-session-coordination.md` before the first browser_* call — shared Playwright instance, tab ownership, persisted login.
You are driving a real, visible browser to feel what a user feels. A green script proves nothing about UX — so see the screen and watch the logs.
user-playwright MCP runs headed by default — neverpass --headless. Drive the on-screen window; if you can't see it, say so.
(browser_click, browser_type, browser_fill_form, browser_select_option, browser_hover, browser_press_key, browser_drag) exactly as a user would. Never chain a whole flow into one code snippet.
ONLY to read state (DOM, computed styles, storage, perf) or restore auth in --isolated mode — never to click, type, navigate, or submit. Driving the UI through code bypasses the real events and hides the bug you're hunting.
*.spec.ts, run npx playwright test, oruse codegen. You are here to experience the flow, not automate past it.
browser_snapshot + browser_take_screenshot(graphics) + browser_console_messages (logs) + browser_network_requests + the dev-server terminal. Real pain points surface on the screen and in the logs, not in an assertion.
After every browser_navigate:
browser_wait_for({ time: 2 }) — short initial wait (2 seconds)browser_snapshot — verify URL changed and page has contentbrowser_wait_for({ time: 2 }) + browser_snapshot againNever assume navigation succeeded without a snapshot to confirm it.
browser_wait_for({ time: N }) → N must be ≤ 3browser_wait_for({ text: "...", timeout: 5000 }) → always set explicit timeout (default is 30000ms which is way too long)browser_wait_for({ textGone: "...", timeout: 5000 }) → same ruleUnit reminder: time is in SECONDS, timeout is in MILLISECONDS.
wait 2s → snapshot → check condition
↓ not ready
wait 2s → snapshot → check condition
↓ not ready
wait 2s → snapshot → check condition
↓ still not ready
STOP → report blocker with evidenceThis handles Vercel cold starts (5-15s), SPA hydration, and slow APIs without ever blocking blindly.
Track attempts for each interaction goal (e.g. "click login button"):
| Attempts | Action |
|---|---|
| 1 | Try the action normally |
| 2 | If same result, try alternative (different ref, scroll into view, browser_search) |
| 3 | Gather evidence: browser_console_messages + browser_network_requests |
| 4 | STOP. Report what blocked progress with evidence. |
Never repeat the exact same failing action without new evidence.
When something isn't working, gather evidence FIRST — then form a hypothesis:
browser_console_messages — JS errors, failed assertionsbrowser_network_requests — pending/failed API calls, CORS errorsbrowser_snapshot — actual DOM state (not what you assume)browser_take_screenshot — visual state for layout/rendering issuesOnly retry after you have a new hypothesis based on this evidence.
| Scope | Max time |
|---|---|
| Single page interaction (click, fill, select) | 15 seconds |
| Page navigation + verification | 30 seconds |
| Multi-page test flow | 5 minutes |
| Full test suite | 15 minutes |
If a step exceeds its budget, skip it and log [TIMEOUT] skipped: <step>. Do not let one stuck step kill the entire session.
SPAs (React, Next.js, Vue) fire load before hydration completes. Do NOT rely on page load events. Instead:
browser_wait_for({ text: "Dashboard", timeout: 8000 }) for key landmarksbrowser_wait_for({ textGone: "Loading", timeout: 8000 })After ANY action that could change the page (navigate, click, fill, select, hover, press key, wait, dialog response), take a fresh `browser_snapshot` before the next interaction. Old refs are invalid after state changes.
The user-playwright server exposes one browser for all agents in this Cursor instance. There is no lock/unlock API on this server — coordinate with tabs instead.
browser_tabs list → read .playwright-mcp/session.json → select auth tab OR new tab
→ work only in your tab → save storage state → update session.jsonbrowser_navigate blindly on the default tab — another agent may be mid-test.browser_close tabs you did not open.references/playwright-session-coordination.md.browser_lock), use lock/unlock there; this skill'stab rules still apply when sharing tabs across chats.
browser_navigate → browser_lock({ action: "lock" }) → interactions → browser_lock({ action: "unlock" })browser_tabs list), lock FIRST before interactingWhen you must stop, report exactly:
BLOCKER:
- Page: [current URL]
- Goal: [what I was trying to do]
- Blocked by: [what prevented it]
- Evidence: [console errors / network failures / screenshot observation]
- Suggestion: [most likely next step or manual action needed]This gives the user actionable information instead of a silent freeze.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.