mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-10-11 01:28:57 +02:00
added scripts feature
Signed-off-by: RonniSkansing <rskansing@gmail.com>
This commit is contained in:
37 files changed
+4319
-82
No files matched your search
@@ -0,0 +1,102 @@
|
||||
package script
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestEntry is one line in the run log. It mirrors the remote browser editor's
|
||||
// runLog entry shape so the editor renders both the same way.
|
||||
// - type "log": Message (+ optional Data)
|
||||
// - type "info": Message
|
||||
// - type "event": Key (event name) + Value (event data) — an emitEvent call
|
||||
// - type "error": Message
|
||||
// - type "done": end marker
|
||||
type TestEntry struct {
|
||||
Type string `json:"type"`
|
||||
Time string `json:"time"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// TestResult is the captured output of a test run: an ordered run log plus the
|
||||
// terminal error, if any.
|
||||
type TestResult struct {
|
||||
OK bool `json:"ok"`
|
||||
DurationMs int64 `json:"durationMs"`
|
||||
Entries []TestEntry `json:"entries"`
|
||||
Error string `json:"error,omitempty"`
|
||||
ErrorPhase string `json:"errorPhase,omitempty"`
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// maxTestEntries caps the captured run log so a script that logs in a tight loop
|
||||
// cannot grow the response unboundedly and exhaust memory in the request handler.
|
||||
const maxTestEntries = 2000
|
||||
|
||||
func (t *TestResult) add(e TestEntry) {
|
||||
e.Time = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if len(t.Entries) >= maxTestEntries {
|
||||
if len(t.Entries) == maxTestEntries {
|
||||
t.Entries = append(t.Entries, TestEntry{
|
||||
Type: "log",
|
||||
Time: e.Time,
|
||||
Message: "… output truncated (too many entries)",
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Entries = append(t.Entries, e)
|
||||
}
|
||||
|
||||
func (t *TestResult) addLog(msg string, data interface{}) {
|
||||
t.add(TestEntry{Type: "log", Message: msg, Data: data})
|
||||
}
|
||||
|
||||
func (t *TestResult) addInfo(msg string, data interface{}) {
|
||||
t.add(TestEntry{Type: "info", Message: msg, Data: data})
|
||||
}
|
||||
|
||||
func (t *TestResult) addEvent(name string, data map[string]interface{}) {
|
||||
t.add(TestEntry{Type: "event", Key: name, Value: data})
|
||||
}
|
||||
|
||||
func (t *TestResult) addFetchOK(method, url string, status int) {
|
||||
t.add(TestEntry{Type: "log", Message: fmt.Sprintf("%s %s → %d", method, url, status)})
|
||||
}
|
||||
|
||||
func (t *TestResult) addFetchErr(method, url, errMsg string) {
|
||||
t.add(TestEntry{Type: "log", Message: fmt.Sprintf("%s %s ✗ %s", method, url, errMsg)})
|
||||
}
|
||||
|
||||
func (t *TestResult) setError(phase, msg string) {
|
||||
t.mu.Lock()
|
||||
if t.Error == "" {
|
||||
t.Error = msg
|
||||
t.ErrorPhase = phase
|
||||
}
|
||||
t.mu.Unlock()
|
||||
t.add(TestEntry{Type: "error", Message: msg})
|
||||
}
|
||||
|
||||
// RunTest runs a script against a simulated event and captures what it does as
|
||||
// an ordered run log, without touching the campaign: log, info and emitEvent are
|
||||
// recorded rather than applied, and no webhooks fire. http.fetch runs for real.
|
||||
// The Runner's Timeout bounds the run.
|
||||
func (r *Runner) RunTest(script string, event EventContext) *TestResult {
|
||||
res := &TestResult{Entries: []TestEntry{}}
|
||||
start := time.Now()
|
||||
r.run(Job{ScriptID: "test", Script: script, Event: event, test: res})
|
||||
res.DurationMs = time.Since(start).Milliseconds()
|
||||
res.OK = res.Error == ""
|
||||
if res.OK {
|
||||
res.add(TestEntry{Type: "done"})
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in new issue
Block a user