feat: introduce nucleijs utils

dev
Tarun Koyalwar 2024-01-23 04:11:04 +05:30
parent 3b5ce39e86
commit 2c2cc2774a
5 changed files with 220 additions and 4 deletions

View File

@ -16,12 +16,14 @@ var (
dir string dir string
generatedDir string generatedDir string
targetModules string targetModules string
goOnly bool
) )
func main() { func main() {
flag.StringVar(&dir, "dir", "libs", "directory to process") flag.StringVar(&dir, "dir", "libs", "directory to process")
flag.StringVar(&generatedDir, "out", "generated", "directory to output generated files") flag.StringVar(&generatedDir, "out", "generated", "directory to output generated files")
flag.StringVar(&targetModules, "target", "", "target modules to generate") flag.StringVar(&targetModules, "target", "", "target modules to generate")
flag.BoolVar(&goOnly, "go", false, "generate only go files")
flag.Parse() flag.Parse()
log.SetFlags(0) log.SetFlags(0)
if !fileutil.FolderExists(dir) { if !fileutil.FolderExists(dir) {
@ -52,10 +54,12 @@ func process() error {
} }
prefixed := "lib" + module prefixed := "lib" + module
if !goOnly {
err = data.WriteJSTemplate(filepath.Join(generatedDir, "js/"+prefixed), module) err = data.WriteJSTemplate(filepath.Join(generatedDir, "js/"+prefixed), module)
if err != nil { if err != nil {
return fmt.Errorf("could not write js template: %v", err) return fmt.Errorf("could not write js template: %v", err)
} }
}
err = data.WriteGoTemplate(path.Join(generatedDir, "go/"+prefixed), module) err = data.WriteGoTemplate(path.Join(generatedDir, "go/"+prefixed), module)
if err != nil { if err != nil {
return fmt.Errorf("could not write go template: %v", err) return fmt.Errorf("could not write go template: %v", err)

View File

@ -346,6 +346,10 @@ func (d *TemplateData) handleStarExpr(v *ast.StarExpr) string {
} }
func (d *TemplateData) collectTypeFromExternal(pkg *types.Package, pkgName, name string) { func (d *TemplateData) collectTypeFromExternal(pkg *types.Package, pkgName, name string) {
if pkgName == "goja" {
// no need to attempt to collect types from goja ( this is metadata )
return
}
extra := PackageTypeExtra{ extra := PackageTypeExtra{
Fields: make(map[string]string), Fields: make(map[string]string),
} }

View File

@ -15,16 +15,22 @@ func init() {
module.Set( module.Set(
gojs.Objects{ gojs.Objects{
// Functions // Functions
"Client": lib_kerberos.NewKerberosClient,
// Var and consts // Var and consts
// Types (value type) // Types (value type)
// "Client": func() lib_kerberos.Client { return lib_kerberos.Client{} },
"EnumerateUserResponse": func() lib_kerberos.EnumerateUserResponse { return lib_kerberos.EnumerateUserResponse{} }, "EnumerateUserResponse": func() lib_kerberos.EnumerateUserResponse { return lib_kerberos.EnumerateUserResponse{} },
"KerberosClient": func() lib_kerberos.KerberosClient { return lib_kerberos.KerberosClient{} }, "KerberosClient": func() lib_kerberos.KerberosClient { return lib_kerberos.KerberosClient{} },
"ServiceOptions": func() lib_kerberos.ServiceOptions { return lib_kerberos.ServiceOptions{} },
"TGS": func() lib_kerberos.TGS { return lib_kerberos.TGS{} },
// Types (pointer type) // Types (pointer type)
"NewClient": func() *lib_kerberos.Client { return &lib_kerberos.Client{} },
"NewEnumerateUserResponse": func() *lib_kerberos.EnumerateUserResponse { return &lib_kerberos.EnumerateUserResponse{} }, "NewEnumerateUserResponse": func() *lib_kerberos.EnumerateUserResponse { return &lib_kerberos.EnumerateUserResponse{} },
"NewKerberosClient": func() *lib_kerberos.KerberosClient { return &lib_kerberos.KerberosClient{} }, "NewServiceOptions": func() *lib_kerberos.ServiceOptions { return &lib_kerberos.ServiceOptions{} },
"NewTGS": func() *lib_kerberos.TGS { return &lib_kerberos.TGS{} },
}, },
).Register() ).Register()
} }

View File

@ -0,0 +1,56 @@
package kerberos
import (
"fmt"
"github.com/dop251/goja"
"github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
)
// Updated Package definations and structure
type Client struct {
nj *utils.NucleiJS // helper functions/bindings
}
// Constructor for KerberosClient
// creates client object and can be created using new
// var client = new kerberos.Client(domain,controller);
func NewKerberosClient(call goja.ConstructorCall, runtime *goja.Runtime) *goja.Object {
// setup nucleijs utils
c := &Client{nj: utils.NewNucleiJS(runtime)}
c.nj.ObjectSig = "Client(domain, controller)" // will be included in error messages
// get arguments (type assertion is efficient than reflection)
// when accepting type as input like net.Conn we can use utils.GetArg
domain, _ := c.nj.GetArg(call.Arguments, 0).(string)
controller, _ := c.nj.GetArg(call.Arguments, 1).(string)
// validate arguments
c.nj.Require(domain != "", "domain cannot be empty")
c.nj.Require(controller != "", "controller cannot be empty")
// Link Constructor to Client and return
return utils.LinkConstructor(call, runtime, c)
}
// EnumerateUserResponse is the response from EnumerateUsers
func (c *Client) EnumerateUser(username string) (EnumerateUserResponse, error) {
return EnumerateUserResponse{}, nil
}
type ServiceOptions struct {
Username string
Password string
Target string
SPN string
}
func (c *Client) GetServiceTicket(sv ServiceOptions) (TGS, error) {
fmt.Printf("get service ticket %v\n", sv)
return TGS{}, nil
}
// prefer using string or hex over byte array in javascript modules
func (c *Client) Send(data string) (string, error) {
return "", nil
}

146
pkg/js/utils/nucleijs.go Normal file
View File

@ -0,0 +1,146 @@
package utils
import (
"fmt"
"reflect"
"sync"
"github.com/dop251/goja"
)
// temporary on demand runtime to throw errors when vm is not available
var (
tmpRuntime *goja.Runtime
runtimeInit func() = sync.OnceFunc(func() {
tmpRuntime = goja.New()
})
)
func getRuntime() *goja.Runtime {
runtimeInit()
return tmpRuntime
}
// NucleiJS is js bindings that handles goja runtime related issue
// and allows setting a defer statements to close resources
type NucleiJS struct {
vm *goja.Runtime
ObjectSig string
}
// NewNucleiJS creates a new nucleijs instance
func NewNucleiJS(vm *goja.Runtime) *NucleiJS {
return &NucleiJS{vm: vm}
}
// internal runtime getter
func (j *NucleiJS) runtime() *goja.Runtime {
if j == nil {
return getRuntime()
}
return j.vm
}
// see: https://arc.net/l/quote/wpenftpc for throwing docs
// ThrowError throws an error in goja runtime
func (j *NucleiJS) ThrowError(err error) {
if err == nil {
return
}
panic(j.runtime().ToValue(err.Error()))
}
// Throw throws an error in goja runtime
func (j *NucleiJS) Throw(format string, args ...interface{}) {
panic(j.runtime().ToValue(fmt.Sprintf(format, args...)))
}
// GetArg returns argument at index from goja runtime if not found throws error
func (j *NucleiJS) GetArg(args []goja.Value, index int) any {
if index >= len(args) {
j.Throw("Missing argument at index %v: %v", index, j.ObjectSig)
}
val := args[index]
if goja.IsUndefined(val) {
j.Throw("Missing argument at index %v: %v", index, j.ObjectSig)
}
return val.Export()
}
// GetArgSafe returns argument at index from goja runtime if not found returns default value
func (j *NucleiJS) GetArgSafe(args []goja.Value, index int, defaultValue any) any {
if index >= len(args) {
return defaultValue
}
val := args[index]
if goja.IsUndefined(val) {
return defaultValue
}
return val.Export()
}
// Require throws an error if expression is false
func (j *NucleiJS) Require(expr bool, msg string) {
if !expr {
j.Throw(msg)
}
}
// LinkConstructor links a type with invocation doing this allows
// usage of instance of type in js
func LinkConstructor[T any](call goja.ConstructorCall, vm *goja.Runtime, obj T) *goja.Object {
instance := vm.ToValue(obj).(*goja.Object)
_ = instance.SetPrototype(call.This.Prototype())
return instance
}
// GetStructType gets a type defined in go and passed as argument from goja runtime if not found throws error
// Donot use this unless you are accepting a struct type from constructor
func GetStructType[T any](nj *NucleiJS, args []goja.Value, index int, FuncSig string) T {
if nj == nil {
nj = &NucleiJS{}
}
if index >= len(args) {
if FuncSig == "" {
nj.Throw("Missing argument at index %v", index)
}
nj.Throw("Missing arguments expected : %v", FuncSig)
}
value := args[index]
// validate type
var ptr T
expected := reflect.ValueOf(ptr).Type()
argType := expected.Name()
valueType := value.ExportType().Name()
if argType != valueType {
nj.Throw("Type Mismatch expected %v got %v", argType, valueType)
}
ptrValue := reflect.New(expected).Elem()
ptrValue.Set(reflect.ValueOf(value.Export()))
return ptrValue.Interface().(T)
}
// GetStructTypeSafe gets an type defined in go and passed as argument from goja runtime if not found returns default value
// Donot use this unless you are accepting a struct type from constructor
func GetStructTypeSafe[T any](nj *NucleiJS, args []goja.Value, index int, defaultValue T) T {
if nj == nil {
nj = &NucleiJS{}
}
if index >= len(args) {
return defaultValue
}
value := args[index]
// validate type
var ptr T
argType := reflect.ValueOf(ptr).Type().Name()
valueType := value.ExportType().Name()
if argType != valueType {
return defaultValue
}
return value.ToObject(nj.runtime()).Export().(T)
}