mirror of
https://github.com/anotherhadi/spilltea.git
synced 2026-05-20 01:32:33 +02:00
e8e64eff12
Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
207 lines
4.7 KiB
Go
207 lines
4.7 KiB
Go
package plugins
|
|
|
|
import (
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/anotherhadi/spilltea/internal/db"
|
|
goproxy "github.com/lqqyt2423/go-mitmproxy/proxy"
|
|
lua "github.com/yuin/gopher-lua"
|
|
)
|
|
|
|
func newLuaState(mgr *Manager, p *Plugin) *lua.LState {
|
|
L := lua.NewState()
|
|
registerUtilities(L, mgr, p)
|
|
return L
|
|
}
|
|
|
|
func registerUtilities(L *lua.LState, mgr *Manager, p *Plugin) {
|
|
L.SetGlobal("log", L.NewFunction(func(L *lua.LState) int {
|
|
msg := L.CheckString(1)
|
|
log.Printf("[plugin:%s] %s", p.Name, msg)
|
|
return 0
|
|
}))
|
|
|
|
L.SetGlobal("notif", L.NewFunction(func(L *lua.LState) int {
|
|
title := L.CheckString(1)
|
|
body := L.CheckString(2)
|
|
select {
|
|
case mgr.Notifs <- PluginNotifMsg{Title: title, Body: body}:
|
|
default:
|
|
}
|
|
return 0
|
|
}))
|
|
|
|
L.SetGlobal("create_finding", L.NewFunction(func(L *lua.LState) int {
|
|
t := L.CheckTable(1)
|
|
title := luaTableString(t, "title")
|
|
desc := luaTableString(t, "description")
|
|
key := luaTableString(t, "key")
|
|
severity := luaTableString(t, "severity")
|
|
if severity == "" {
|
|
severity = "info"
|
|
}
|
|
if key == "" {
|
|
key = title
|
|
}
|
|
if mgr.db == nil {
|
|
return 0
|
|
}
|
|
inserted, err := mgr.db.UpsertFinding(db.Finding{
|
|
PluginName: p.Name,
|
|
DedupKey: key,
|
|
Title: title,
|
|
Description: desc,
|
|
Severity: severity,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
if err != nil {
|
|
log.Printf("[plugin:%s] create_finding error: %v", p.Name, err)
|
|
return 0
|
|
}
|
|
_ = inserted
|
|
return 0
|
|
}))
|
|
|
|
L.SetGlobal("is_in_scope", L.NewFunction(func(L *lua.LState) int {
|
|
raw := L.CheckString(1)
|
|
if mgr.broker == nil {
|
|
L.Push(lua.LTrue)
|
|
return 1
|
|
}
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
L.Push(lua.LFalse)
|
|
return 1
|
|
}
|
|
path := u.Path
|
|
if path == "" {
|
|
path = "/"
|
|
}
|
|
L.Push(lua.LBool(mgr.broker.IsInScope(u.Host + path)))
|
|
return 1
|
|
}))
|
|
|
|
L.SetGlobal("quit", L.NewFunction(func(L *lua.LState) int {
|
|
reason := L.OptString(1, "plugin requested quit")
|
|
select {
|
|
case mgr.Quit <- reason:
|
|
default:
|
|
}
|
|
return 0
|
|
}))
|
|
}
|
|
|
|
func luaTableString(t *lua.LTable, key string) string {
|
|
v := t.RawGetString(key)
|
|
if s, ok := v.(lua.LString); ok {
|
|
return string(s)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func pushRequest(L *lua.LState, f *goproxy.Flow) *lua.LTable {
|
|
t := L.NewTable()
|
|
r := f.Request
|
|
L.SetField(t, "method", lua.LString(r.Method))
|
|
L.SetField(t, "url", lua.LString(r.URL.String()))
|
|
L.SetField(t, "host", lua.LString(r.URL.Host))
|
|
L.SetField(t, "path", lua.LString(r.URL.Path))
|
|
|
|
headers := L.NewTable()
|
|
for k, vals := range r.Header {
|
|
L.SetField(headers, k, lua.LString(strings.Join(vals, ", ")))
|
|
}
|
|
L.SetField(t, "headers", headers)
|
|
|
|
L.SetField(t, "get_body", L.NewFunction(func(L *lua.LState) int {
|
|
L.Push(lua.LString(string(r.Body)))
|
|
return 1
|
|
}))
|
|
|
|
L.SetField(t, "set_header", L.NewFunction(func(L *lua.LState) int {
|
|
name := L.CheckString(2)
|
|
value := L.CheckString(3)
|
|
r.Header.Set(name, value)
|
|
return 0
|
|
}))
|
|
|
|
L.SetField(t, "set_body", L.NewFunction(func(L *lua.LState) int {
|
|
body := L.CheckString(2)
|
|
r.Body = []byte(body)
|
|
return 0
|
|
}))
|
|
|
|
return t
|
|
}
|
|
|
|
func pushResponse(L *lua.LState, f *goproxy.Flow) *lua.LTable {
|
|
t := L.NewTable()
|
|
if f.Response == nil {
|
|
return t
|
|
}
|
|
resp := f.Response
|
|
L.SetField(t, "status_code", lua.LNumber(resp.StatusCode))
|
|
|
|
headers := L.NewTable()
|
|
for k, vals := range resp.Header {
|
|
L.SetField(headers, k, lua.LString(strings.Join(vals, ", ")))
|
|
}
|
|
L.SetField(t, "headers", headers)
|
|
|
|
L.SetField(t, "get_body", L.NewFunction(func(L *lua.LState) int {
|
|
L.Push(lua.LString(string(resp.Body)))
|
|
return 1
|
|
}))
|
|
|
|
L.SetField(t, "set_header", L.NewFunction(func(L *lua.LState) int {
|
|
name := L.CheckString(2)
|
|
value := L.CheckString(3)
|
|
resp.Header.Set(name, value)
|
|
return 0
|
|
}))
|
|
|
|
L.SetField(t, "set_body", L.NewFunction(func(L *lua.LState) int {
|
|
body := L.CheckString(2)
|
|
resp.Body = []byte(body)
|
|
return 0
|
|
}))
|
|
|
|
return t
|
|
}
|
|
|
|
func pushEntry(L *lua.LState, e db.Entry) *lua.LTable {
|
|
t := L.NewTable()
|
|
L.SetField(t, "id", lua.LNumber(e.ID))
|
|
L.SetField(t, "method", lua.LString(e.Method))
|
|
L.SetField(t, "host", lua.LString(e.Host))
|
|
L.SetField(t, "path", lua.LString(e.Path))
|
|
L.SetField(t, "status_code", lua.LNumber(e.StatusCode))
|
|
L.SetField(t, "timestamp", lua.LString(e.Timestamp.Format("2006-01-02 15:04:05")))
|
|
L.SetField(t, "request_raw", lua.LString(e.RequestRaw))
|
|
L.SetField(t, "response_raw", lua.LString(e.ResponseRaw))
|
|
return t
|
|
}
|
|
|
|
func callHook(p *Plugin, hookName string, args ...lua.LValue) (string, error) {
|
|
fn := p.L.GetGlobal(hookName)
|
|
if fn == lua.LNil {
|
|
return "", nil
|
|
}
|
|
if err := p.L.CallByParam(lua.P{
|
|
Fn: fn,
|
|
NRet: 1,
|
|
Protect: true,
|
|
}, args...); err != nil {
|
|
return "", err
|
|
}
|
|
ret := p.L.Get(-1)
|
|
p.L.Pop(1)
|
|
if s, ok := ret.(lua.LString); ok {
|
|
return string(s), nil
|
|
}
|
|
return "", nil
|
|
}
|