nuclei/pkg/js/devtools/tsgen/scrape.go

193 lines
5.7 KiB
Go
Raw Normal View History

package tsgen
import (
"fmt"
"go/types"
"regexp"
"strings"
"github.com/projectdiscovery/utils/errkit"
)
// scrape.go scrapes all information of exported type from different package
func (p *EntityParser) scrapeAndCreate(typeName string) error {
if p.newObjects[typeName] == nil {
return nil
}
// get package name
pkgName := strings.Split(typeName, ".")[0]
baseTypeName := strings.Split(typeName, ".")[1]
// get package
pkg, ok := p.imports[pkgName]
if !ok {
return errkit.Newf("package %v for type %v not found", pkgName, typeName)
}
// get type
obj := pkg.Types.Scope().Lookup(baseTypeName)
if obj == nil {
return errkit.Newf("type %v not found in package %+v", typeName, pkg)
}
// Ensure the object is a type name
typeNameObj, ok := obj.(*types.TypeName)
if !ok {
return errkit.Newf("%v is not a type name", typeName)
}
// Ensure the type is a named struct type
namedStruct, ok := typeNameObj.Type().Underlying().(*types.Struct)
if !ok {
return fmt.Errorf("%s is not a named struct type", typeName)
}
// fmt.Printf("got named struct %v\n", namedStruct)
// Iterate over the struct fields
d := &ExtObject{
builtIn: make(map[string]string),
nested: map[string]map[string]*ExtObject{},
}
// fmt.Printf("fields %v\n", namedStruct.NumFields())
for i := 0; i < namedStruct.NumFields(); i++ {
field := namedStruct.Field(i)
fieldName := field.Name()
if field.Exported() {
recursiveScrapeType(nil, fieldName, field.Type(), d)
}
}
entityMap := make(map[string]Entity)
// convert ExtObject to Entity
properties := ConvertExtObjectToEntities(d, entityMap)
entityMap[baseTypeName] = Entity{
Name: baseTypeName,
Type: "interface",
Description: fmt.Sprintf("%v Interface", baseTypeName),
Object: Interface{
Properties: properties,
},
}
for _, entity := range entityMap {
p.entities = append(p.entities, entity)
}
return nil
}
type ExtObject struct {
builtIn map[string]string
nested map[string]map[string]*ExtObject // Changed to map of field names to ExtObject
}
func recursiveScrapeType(parentType types.Type, fieldName string, fieldType types.Type, extObject *ExtObject) {
if named, ok := fieldType.(*types.Named); ok && !named.Obj().Exported() {
// fmt.Printf("type %v is not exported\n", named.Obj().Name())
return
}
if fieldType.String() == "time.Time" {
extObject.builtIn[fieldName] = "Date"
return
}
switch t := fieldType.Underlying().(type) {
case *types.Pointer:
// fmt.Printf("type %v is a pointer\n", fieldType)
recursiveScrapeType(nil, fieldName, t.Elem(), extObject)
case *types.Signature:
// fmt.Printf("type %v is a callback or interface\n", fieldType)
case *types.Basic:
// Check for basic types (built-in types)
if parentType != nil {
switch p := parentType.Underlying().(type) {
case *types.Slice:
extObject.builtIn[fieldName] = "[]" + fieldType.String()
case *types.Array:
extObject.builtIn[fieldName] = fmt.Sprintf("[%v]", p.Len()) + fieldType.String()
}
} else {
extObject.builtIn[fieldName] = fieldType.String()
}
case *types.Struct:
// Check for struct types
if extObject.nested[fieldName] == nil {
// @tarunKoyalwar: it currently does not supported struct arrays
extObject.nested[fieldName] = make(map[string]*ExtObject)
}
nestedExtObject := &ExtObject{
builtIn: make(map[string]string),
nested: map[string]map[string]*ExtObject{},
}
extObject.nested[fieldName][fieldType.String()] = nestedExtObject
for i := 0; i < t.NumFields(); i++ {
field := t.Field(i)
if field.Exported() {
recursiveScrapeType(nil, field.Name(), field.Type(), nestedExtObject)
}
}
case *types.Array:
// fmt.Printf("type %v is an array\n", fieldType)
// get array type
recursiveScrapeType(t, fieldName, t.Elem(), extObject)
case *types.Slice:
// fmt.Printf("type %v is a slice\n", fieldType)
// get slice type
recursiveScrapeType(t, fieldName, t.Elem(), extObject)
default:
// fmt.Printf("type %v is not a builtIn or struct\n", fieldType)
}
}
var re = regexp.MustCompile(`\[[0-9]+\].*`)
// ConvertExtObjectToEntities recursively converts an ExtObject to a list of Entity objects
func ConvertExtObjectToEntities(extObj *ExtObject, nestedTypes map[string]Entity) []Property {
var properties []Property
// Iterate over the built-in types
for fieldName, fieldType := range extObj.builtIn {
var description string
if re.MatchString(fieldType) {
// if it is a fixed size array add len in description
description = fmt.Sprintf("fixed size array of length: %v", fieldType[:strings.Index(fieldType, "]")+1])
// remove length from type
fieldType = "[]" + fieldType[strings.Index(fieldType, "]")+1:]
}
if strings.Contains(fieldType, "time.Duration") {
description = "time in nanoseconds"
}
px := Property{
Name: fieldName,
Type: toTsTypes(fieldType),
Description: description,
}
if strings.HasPrefix(px.Type, "[") {
px.Type = fieldType[strings.Index(px.Type, "]")+1:] + "[]"
}
properties = append(properties, px)
}
// Iterate over the nested types
for fieldName, nestedExtObjects := range extObj.nested {
for origType, nestedExtObject := range nestedExtObjects {
// fix:me this nestedExtObject always has only one element
got := ConvertExtObjectToEntities(nestedExtObject, nestedTypes)
baseTypename := origType[strings.LastIndex(origType, ".")+1:]
// create new nestedType
nestedTypes[baseTypename] = Entity{
Name: baseTypename,
Description: fmt.Sprintf("%v Interface", baseTypename),
Type: "interface",
Object: Interface{
Properties: got,
},
}
// assign current field type to nested type
properties = append(properties, Property{
Name: fieldName,
Type: baseTypename,
})
}
}
return properties
}