github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/module/wasm/sdk.go (about)

     1  //go:build tinygo.wasm
     2  
     3  package wasm
     4  
     5  // This package is designed to be imported by WASM modules.
     6  // TinyGo can build this package, but Go cannot.
     7  
     8  import (
     9  	"fmt"
    10  	"reflect"
    11  	"unsafe"
    12  
    13  	"github.com/mailru/easyjson"
    14  
    15  	"github.com/devseccon/trivy/pkg/module/api"
    16  	"github.com/devseccon/trivy/pkg/module/serialize"
    17  )
    18  
    19  func Debug(message string) {
    20  	message = fmt.Sprintf("Module %s: %s", module.Name(), message)
    21  	ptr, size := stringToPtr(message)
    22  	_debug(ptr, size)
    23  }
    24  
    25  func Info(message string) {
    26  	message = fmt.Sprintf("Module %s: %s", module.Name(), message)
    27  	ptr, size := stringToPtr(message)
    28  	_info(ptr, size)
    29  }
    30  
    31  func Warn(message string) {
    32  	message = fmt.Sprintf("Module %s: %s", module.Name(), message)
    33  	ptr, size := stringToPtr(message)
    34  	_warn(ptr, size)
    35  }
    36  
    37  func Error(message string) {
    38  	message = fmt.Sprintf("Module %s: %s", module.Name(), message)
    39  	ptr, size := stringToPtr(message)
    40  	_error(ptr, size)
    41  }
    42  
    43  //go:wasm-module env
    44  //export debug
    45  func _debug(ptr uint32, size uint32)
    46  
    47  //go:wasm-module env
    48  //export info
    49  func _info(ptr uint32, size uint32)
    50  
    51  //go:wasm-module env
    52  //export warn
    53  func _warn(ptr uint32, size uint32)
    54  
    55  //go:wasm-module env
    56  //export error
    57  func _error(ptr uint32, size uint32)
    58  
    59  var module api.Module
    60  
    61  func RegisterModule(p api.Module) {
    62  	module = p
    63  }
    64  
    65  //export name
    66  func _name() uint64 {
    67  	name := module.Name()
    68  	ptr, size := stringToPtr(name)
    69  	return (uint64(ptr) << uint64(32)) | uint64(size)
    70  }
    71  
    72  //export api_version
    73  func _apiVersion() uint32 {
    74  	return api.Version
    75  }
    76  
    77  //export version
    78  func _version() uint32 {
    79  	return uint32(module.Version())
    80  }
    81  
    82  //export is_analyzer
    83  func _isAnalyzer() uint64 {
    84  	if _, ok := module.(api.Analyzer); !ok {
    85  		return 0
    86  	}
    87  	return 1
    88  }
    89  
    90  //export required
    91  func _required() uint64 {
    92  	files := module.(api.Analyzer).RequiredFiles()
    93  	ss := serialize.StringSlice(files)
    94  	return marshal(ss)
    95  }
    96  
    97  //export analyze
    98  func _analyze(ptr, size uint32) uint64 {
    99  	filePath := ptrToString(ptr, size)
   100  	custom, err := module.(api.Analyzer).Analyze(filePath)
   101  	if err != nil {
   102  		Error(fmt.Sprintf("analyze error: %s", err))
   103  		return 0
   104  	}
   105  	return marshal(custom)
   106  }
   107  
   108  //export is_post_scanner
   109  func _isPostScanner() uint64 {
   110  	if _, ok := module.(api.PostScanner); !ok {
   111  		return 0
   112  	}
   113  	return 1
   114  }
   115  
   116  //export post_scan_spec
   117  func _post_scan_spec() uint64 {
   118  	return marshal(module.(api.PostScanner).PostScanSpec())
   119  }
   120  
   121  //export post_scan
   122  func _post_scan(ptr, size uint32) uint64 {
   123  	var results serialize.Results
   124  	if err := unmarshal(ptr, size, &results); err != nil {
   125  		Error(fmt.Sprintf("post scan error: %s", err))
   126  		return 0
   127  	}
   128  
   129  	results, err := module.(api.PostScanner).PostScan(results)
   130  	if err != nil {
   131  		Error(fmt.Sprintf("post scan error: %s", err))
   132  		return 0
   133  	}
   134  	return marshal(results)
   135  }
   136  
   137  func marshal(v easyjson.Marshaler) uint64 {
   138  	b, err := easyjson.Marshal(v)
   139  	if err != nil {
   140  		Error(fmt.Sprintf("marshal error: %s", err))
   141  		return 0
   142  	}
   143  
   144  	p := uintptr(unsafe.Pointer(&b[0]))
   145  	return (uint64(p) << uint64(32)) | uint64(len(b))
   146  }
   147  
   148  func unmarshal(ptr, size uint32, v easyjson.Unmarshaler) error {
   149  	var b []byte
   150  	s := (*reflect.SliceHeader)(unsafe.Pointer(&b))
   151  	s.Len = uintptr(size)
   152  	s.Cap = uintptr(size)
   153  	s.Data = uintptr(ptr)
   154  
   155  	if err := easyjson.Unmarshal(b, v); err != nil {
   156  		return fmt.Errorf("unmarshal error: %s", err)
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  // ptrToString returns a string from WebAssembly compatible numeric types representing its pointer and length.
   163  func ptrToString(ptr uint32, size uint32) string {
   164  	// Get a slice view of the underlying bytes in the stream. We use SliceHeader, not StringHeader
   165  	// as it allows us to fix the capacity to what was allocated.
   166  	return *(*string)(unsafe.Pointer(&reflect.SliceHeader{
   167  		Data: uintptr(ptr),
   168  		Len:  uintptr(size), // Tinygo requires these as uintptrs even if they are int fields.
   169  		Cap:  uintptr(size), // ^^ See https://github.com/tinygo-org/tinygo/issues/1284
   170  	}))
   171  }
   172  
   173  // stringToPtr returns a pointer and size pair for the given string in a way compatible with WebAssembly numeric types.
   174  func stringToPtr(s string) (uint32, uint32) {
   175  	buf := []byte(s)
   176  	ptr := &buf[0]
   177  	unsafePtr := uintptr(unsafe.Pointer(ptr))
   178  	return uint32(unsafePtr), uint32(len(buf))
   179  }