mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-07-20 10:41:01 +02:00
add rbp lifecycle events
Signed-off-by: Ronni Skansing <rskansing@gmail.com>
This commit is contained in:
@@ -586,6 +586,10 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) {
|
||||
_, msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
sess.victimConnected.Store(false)
|
||||
select {
|
||||
case runner.Incoming <- remotebrowser.IncomingMsg{Event: "disconnect"}:
|
||||
default:
|
||||
}
|
||||
// keepAlive: browser is parked for operator takeover — a victim
|
||||
// disconnect must not kill the session, the operator still needs it.
|
||||
if !sess.isKeepAlive.Load() {
|
||||
@@ -671,14 +675,34 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) {
|
||||
|
||||
go runner.Run(ctx) //nolint:errcheck
|
||||
|
||||
// As soon as the browser spawns, mark the session as streamable and apply
|
||||
// the victim viewport if it was already received before the browser was ready.
|
||||
// As soon as the browser spawns, mark the session as streamable, apply the
|
||||
// victim viewport, and subscribe to navigation events so the script can react
|
||||
// via s.on("navigate", fn).
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case page := <-runner.BrowserCh:
|
||||
sess.setBrowserPage(page)
|
||||
applyViewport(page)
|
||||
wait := page.EachEvent(
|
||||
func(e *proto.PageFrameNavigated) (stop bool) {
|
||||
if e.Frame != nil && e.Frame.ParentID == "" {
|
||||
select {
|
||||
case runner.Incoming <- remotebrowser.IncomingMsg{Event: "navigate", Data: map[string]string{"url": e.Frame.URL}}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
return
|
||||
},
|
||||
func(e *proto.PageNavigatedWithinDocument) (stop bool) {
|
||||
select {
|
||||
case runner.Incoming <- remotebrowser.IncomingMsg{Event: "navigate", Data: map[string]string{"url": e.URL}}:
|
||||
default:
|
||||
}
|
||||
return
|
||||
},
|
||||
)
|
||||
go wait()
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -767,7 +791,7 @@ func (m *RemoteBrowserController) ServeVictim(g *gin.Context) {
|
||||
processEvent(evt)
|
||||
if evt.Type == "log" || evt.Type == "capture" || evt.Type == "submit" ||
|
||||
evt.Type == "keep_alive" || evt.Type == "info" || evt.Type == "error" ||
|
||||
evt.Type == "screenshot" {
|
||||
evt.Type == "screenshot" || evt.Type == "dom_dump" {
|
||||
continue
|
||||
}
|
||||
payload, err := json.Marshal(map[string]interface{}{
|
||||
|
||||
@@ -1299,6 +1299,30 @@ func RegisterBrowserBindings(vm *goja.Runtime, pc *goja.Object, page *rod.Page,
|
||||
return goja.Undefined()
|
||||
})
|
||||
|
||||
pc.Set("domDump", func(call goja.FunctionCall) goja.Value {
|
||||
name := argStr(call.Argument(0))
|
||||
dbg("→ domDump " + name)
|
||||
rPage, rCancel := readPage()
|
||||
defer rCancel()
|
||||
info, _ := rPage.Info()
|
||||
html, err := rPage.HTML()
|
||||
if err != nil {
|
||||
if emitter != nil {
|
||||
emitter.log(fmt.Sprintf("[domDump] %s: %s", name, err))
|
||||
}
|
||||
return goja.Undefined()
|
||||
}
|
||||
pageURL := ""
|
||||
if info != nil {
|
||||
pageURL = info.URL
|
||||
}
|
||||
if emitter != nil {
|
||||
emitter.domDump(name, html, pageURL)
|
||||
}
|
||||
dbg(fmt.Sprintf("✓ domDump %s (%d bytes)", name, len(html)))
|
||||
return goja.Undefined()
|
||||
})
|
||||
|
||||
pc.Set("setViewport", func(call goja.FunctionCall) goja.Value {
|
||||
w := call.Argument(0).ToInteger()
|
||||
h := call.Argument(1).ToInteger()
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
// RunEvent is an event emitted during script execution.
|
||||
type RunEvent struct {
|
||||
Type string `json:"type"` // "event", "log", "error", "done", "capture", "screenshot", "info", "submit"
|
||||
Type string `json:"type"` // "event", "log", "error", "done", "capture", "screenshot", "dom_dump", "info", "submit"
|
||||
Key string `json:"key,omitempty"` // for type=event/screenshot (label)
|
||||
Value any `json:"value,omitempty"` // for type=event/capture/screenshot/submit (base64 data URI or arbitrary data)
|
||||
URL string `json:"url,omitempty"` // for type=screenshot (page URL at capture time)
|
||||
@@ -74,6 +74,16 @@ func (e *channelEmitter) capture(data interface{}) {
|
||||
})
|
||||
}
|
||||
|
||||
func (e *channelEmitter) domDump(label string, html string, pageURL string) {
|
||||
e.send(RunEvent{
|
||||
Type: "dom_dump",
|
||||
Key: label,
|
||||
Value: html,
|
||||
URL: pageURL,
|
||||
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
||||
})
|
||||
}
|
||||
|
||||
func (e *channelEmitter) info(msg string) {
|
||||
e.send(RunEvent{
|
||||
Type: "info",
|
||||
|
||||
@@ -764,6 +764,9 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
})
|
||||
|
||||
// Event-driven API: s.on(event, fn) + s.listen() + s.done()
|
||||
// Built-in lifecycle events emitted by the server:
|
||||
// "disconnect" — victim WebSocket connection dropped
|
||||
// "navigate" — main frame navigated; data: { url: string }
|
||||
handlers := map[string]goja.Callable{}
|
||||
listenDone := make(chan struct{}, 1)
|
||||
|
||||
|
||||
@@ -485,11 +485,19 @@ interface Session {
|
||||
close(): void;
|
||||
|
||||
// ── Event-driven API ─────────────────────────────────────────────────────
|
||||
/** Register a handler called when the victim page sends this event */
|
||||
/**
|
||||
* Register a handler for a named event. Must be called before s.listen().
|
||||
*
|
||||
* Built-in lifecycle events emitted automatically by the server:
|
||||
* - `"disconnect"` — victim closed their browser or navigated away; no data
|
||||
* - `"navigate"` — main frame navigated to a new URL; data: `{ url: string }`
|
||||
*
|
||||
* All other event names are victim-page events sent via `rb.emit(name, data)`.
|
||||
*/
|
||||
on(event: string, handler: (data: any) => void): void;
|
||||
/** Start processing incoming victim events; blocks until done() is called */
|
||||
/** Start processing incoming events; blocks until done() is called */
|
||||
listen(): void;
|
||||
/** Signal listen() to stop processing */
|
||||
/** Exit the listen() loop */
|
||||
done(): void;
|
||||
|
||||
// ── Race ──────────────────────────────────────────────────────────────────
|
||||
@@ -584,9 +592,11 @@ interface FrameSession {
|
||||
// ── JavaScript evaluation ─────────────────────────────────────────────────
|
||||
evaluate(expression: string): any;
|
||||
|
||||
// ── Screenshots ───────────────────────────────────────────────────────────
|
||||
// ── Screenshots & DOM capture ────────────────────────────────────────────
|
||||
screenshot(name: string): void;
|
||||
screenshotElement(selector: string, name: string): void;
|
||||
/** Capture the full page HTML and emit it as a named dom_dump event for debugging */
|
||||
domDump(name: string): void;
|
||||
|
||||
// ── Viewport & emulation ─────────────────────────────────────────────────
|
||||
setViewport(width: number, height: number): void;
|
||||
|
||||
Reference in New Issue
Block a user