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 }